org.wamblee.cache.ComputedValue Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2005-2010 the original author or authors.
*
* 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 org.wamblee.cache;
/**
* Utility class to deal with recomputation of a certain value. The goal is to
* have only one thread at a time compute the value while other threads that
* simulateneously detect recomputation continue with the old value.
*
*
* @author Erik Brakkee
*
* @param
*/
public class ComputedValue {
/**
* Computation
*
* @param
* Type of object to compute.
*/
public static interface Computation {
/**
* Checks whether the object is out of date. This will be invoked while
* holding the lock passed at construction of the compute guard.
*
* Any runtime exceptions thrown are passed back through the
* {@link ComputedValue#get()}.
*
* @return True iff recomputation is necessary.
*/
boolean isOutOfDate();
/**
* Computes the object. This will be invoked while not holding
* the lock passed at construction of the compute guard. It is
* guaranteed that per ComputeGuard, no concurrent calls to compute()
* are done.
*
* Any runtime exceptions thrown are passed back through the
* {@link ComputedValue#get()}.
*
* @return Computed object.
*/
T compute();
}
private Object lock;
private Computation computedValue;
private Boolean busy;
private T value;
/**
* Constructs the compute guard
*
* @param aLock
* Lock to use during computation and to guard the value.
* @param aComputation
* Computation to use.
*/
public ComputedValue(Object aLock, Computation aComputation) {
lock = aLock;
computedValue = aComputation;
busy = false;
value = null;
}
/**
* Triggers computation of the value (if no other thread is currently
* computing the value).
*/
public void compute() {
synchronized (this) {
if (busy) {
return; // another thread is already taking care of it.
}
busy = true;
}
try {
T newvalue = computedValue.compute();
set(newvalue);
} finally {
synchronized (this) {
busy = false;
}
}
}
/**
* Gets the current value of the object, recomputing it if the object is out
* of date. This method ensures that only one thread at a time will do
* recomputations.
*
* @return Current value.
*/
public T get() {
boolean mustCompute = false;
synchronized (lock) {
if (computedValue.isOutOfDate()) {
mustCompute = true;
}
}
if (mustCompute) {
compute();
}
synchronized (lock) {
return value;
}
}
/**
* Gets the currently cached value. No recomputation is performed.
*
* @return Cached value.
*/
public T getCached() {
synchronized (lock) {
return value;
}
}
/**
* Sets the value explicitly.
*
* @param aValue
* value to set.
*/
public void set(T aValue) {
synchronized (lock) {
value = aValue;
}
}
}