/* * 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; } } }