[concurrency-interest] Scalable object cache

Peter Firmstone peter.firmstone at zeus.net.au
Wed Feb 29 08:37:16 EST 2012


I don't have the hardware to test this so I'm hoping someone on the list
will know.

In the class below, the get() method returns the referent, provided it
hasn't been enqueued.

Both read and clock are volatile, clock is written to, once every 10
seconds (or an interval the developer sets) by a single thread.

if (read < clock) read = clock; //Avoid unnecessary volatile write.

If many threads are retrieving the referent, the volatile read variable
is written at least once in between clock updates, but any further
retrievals will only be reads.

So in this case the volatile long values are multi read, with occasional
writes, does get() look scalable to you?

Regards,

Peter.

/*
 * Copyright 2012 Zeus Project Services Pty Ltd.
 *
 * Licensed 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 au.net.zeus.collection;

/**
 * 
 * @author Peter Firmstone.
 */
class TimedReferrer<T> implements Referrer<T>, TimeBomb {
    
    private volatile long clock;
    private volatile long read;
    private final TimedRefQueue queue;
    private volatile T referent;
    private volatile boolean enqued;
    private final Object lock;
    private final int hash;
    
    TimedReferrer(T k, TimedRefQueue q){
        long time = System.nanoTime();
        clock = time;
        read = time;
        referent = k;
        queue = q;
        enqued = false;
        lock = new Object();
        int hash = 7;
        hash = 29 * hash + k.hashCode();
        hash = 29 * hash + k.getClass().hashCode();
        this.hash = hash;
    }

    public T get() {
        // Doesn't need to be atomic.
        if (read < clock) read = clock; //Avoid unnecessary volatile
write.
        return referent;
    }

    public void clear() {
        referent = null;
    }

    public boolean isEnqueued() {
        return enqued;
    }

    public boolean enqueue() {
        if (enqued) return false;
        if (referent == null) return false;
        if (queue == null) return false;
        synchronized (lock){ // Sync for atomic write of enqued.
            if (enqued) return false;
            enqued = queue.offer(this);
        }
        return enqued;
    }
    
    @Override
    public void updateClock(long time){
        if (read < clock) { // only write volatile if necessary.
            enqueue();
            clear();
        } else {
            clock = time;
        }
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o)  return true; // Same reference.
        if (!(o instanceof Referrer))  return false;
        Object k1 = get();
        Object k2 = ((Referrer) o).get();
        if ( k1 != null && k1.equals(k2)) return true;
        return ( k1 == null && k2 == null && hashCode() ==
o.hashCode()); // Both objects were collected.
    }

    @Override
    public int hashCode() {
        Object k = get();
        int hash = 7;
        if (k != null) {
            hash = 29 * hash + k.hashCode();
            hash = 29 * hash + k.getClass().hashCode();
        } else {
            hash = this.hash;
        }
        return hash;
    }
    
    @Override
    public String toString(){
        Object s = get();
        if (s != null) return s.toString();
        return super.toString();
    }
    
}



More information about the Concurrency-interest mailing list