[concurrency-interest] Reference Collections

Peter Firmstone peter.firmstone at zeus.net.au
Wed Jan 4 16:38:49 EST 2012


On Thu, 2012-01-05 at 06:54, Nathan Reynolds wrote:
> I would be careful in having 1 thread do all of the cleaning.  JRockit
> has an option to allow for multiple finalization threads to run.  One
> finalizer thread couldn't finalize objects fast enough.

Ok that's interesting, one problem though, using more than on thread on
ReferenceQueue will create contention, so there would need to be one
thread that takes from ReferenceQueue, with its own double ended queue,
where other threads are created to steal off the reference queue if it
becomes overloaded.  This is assuming that the bottle neck is the
collection and not the ReferenceQueue.

Regards,

Peter.

This is the single gc thread implementation, in this case there's a
queue for each collection:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.river.impl.util;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * ReferenceProcessor is responsible for creation and collection of
References 
 * on behalf of Reference Collection implementations.
 *
 * @param <T> 
 * @author peter
 */
class ReferenceProcessor<T> implements ReferenceQueuingFactory<T,
Referrer<T>> {
    
    private final Collection<Referrer<T>> col;
    private final ReferenceQueue<T> queue;
    private final Ref type;
    private final Lock queueLock;
    private final Thread cleaner;
    
    ReferenceProcessor(Collection<Referrer<T>> col, Ref type,
ReferenceQueue<T> queue, boolean gcThread){
        if (col == null || type == null ) throw new
NullPointerException("collection or reference type cannot be null");
        this.col = col;
        this.type = type;
        this.queue = type == Ref.STRONG ? null : queue;
        queueLock = new ReentrantLock();
        cleaner = (gcThread && this.queue != null)
                ? new Thread( new CleanerTask(this.col, this.queue),
                                                   
"ReferenceProcessorGC") 
                : null;
        if (cleaner != null){
            cleaner.setDaemon(true);
            cleaner.start();
        }
    }

    @Override
    public T pseudoReferent(Referrer<T> u) {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public Referrer<T> referenced(T w, boolean enque, boolean temporary)
{
        if (w == null) return null;
        if (temporary) return ReferenceFactory.singleUseForLookup(w,
type);
        return ReferenceFactory.create(w, enque == true ? queue : null,
type);
    }

    @Override
    public void processQueue() {
        if (queue == null || cleaner != null) return;
        Object t = null;
        /*
         * The reason for using an explicit lock is if another thread is
         * removing the garbage, we don't want to prevent all other
threads
         * accessing the underlying collection, when it blocks on poll,
         * this means that some client threads will receive null values 
         * on occassion, but this is a small price to pay.  
         * Might have to employ the null object pattern.
         */
        if ( queueLock.tryLock()){
            try {
                while ( (t = queue.poll()) != null){
                    col.remove(t);
                }
            }finally{
                queueLock.unlock();
            }
        }
    }
    
    private class CleanerTask implements Runnable {
        
        private final Collection col;
        private final ReferenceQueue queue;
        
        private CleanerTask(Collection c, ReferenceQueue queue){
            col = c;
            this.queue = queue;
        }
        
        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            Object t = null;
            try {
                // should never be null, remove blocks.
                while ( (t = queue.remove()) != null ){ 
                    col.remove(t);
                }
                if ( t == null) throw new NullPointerException("null
remove from ReferenceQueue");
            } catch (InterruptedException ex) {
                // Restore the interrupt.
                thread.interrupt();
                return;
            }
            
        }
    
    }
}





More information about the Concurrency-interest mailing list