The Artima Developer Community
Sponsored Link

Java Buzz Forum
On how it is OK to be lazy

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Wilfred Springer

Posts: 176
Nickname: springerw
Registered: Sep, 2006

Wilfred Springer is a Software Architect at Xebia
On how it is OK to be lazy Posted: Aug 4, 2008 12:23 AM
Reply to this message Reply

This post originated from an RSS feed registered with Java Buzz by Wilfred Springer.
Original Post: On how it is OK to be lazy
Feed Title: Distributed Reflections of the Third Kind
Feed URL: http://agilejava.com/blog/?feed=atom
Feed Description: Distributed Reflections of the Third Kind
Latest Java Buzz Posts
Latest Java Buzz Posts by Wilfred Springer
Latest Posts From Distributed Reflections of the Third Kind

Advertisement

A couple of days ago, I blogged about a lazy loading caching object reference, and I said I was going to use MultithreadedTC to test if it was behaving OK. First of all, the implementation I uploaded is not threadsafe. Second: Ecto, the OSX based blogging client is awful: I spent half an hour writing up the problems with my first implementation, and detailing how I implemented the new one, saved it in Ecto, and - what do you know - Ecto just conveniently dropped it.

Anyhow, back to the lazy loading caching reference. I think I now have something that works. I don’t have the time to discuss it again entirely, but I will just give you the source code, and a UML diagram that basically explains the basic idea.

package com.tomtom.quarks.util;
	
import java.lang.ref.SoftReference;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
	
/**
 * A <i>reference</i> to a an instance of type {@link T}, to be dynamically
 * loaded, on demand, whenever the reference is resolved by calling
 * {@link #get()}; once loaded, the reference will hold on to the instance of
 * {@link T} until the garbage collector collects it. After that, the instance
 * of {@link T} will - again - be recreated, whenever required.
 *
 * @author Wilfred Springer
 *
 * @param <T>
 *            The type of object referenced.
 */
public class LazyLoadingReference<T> {
	
    /**
     * An {@link AtomicReference} to a {@link SoftReference} to a {@link Future}
     * producing an instance of {@link T}. The {@link AtomicReference} makes
     * sure the value can be updated atomically. The {@link SoftReference} makes
     * sure the garbage collector will release the reference if it needs more
     * memory. The {@link Future} makes sure that - once the value has been set,
     * all calls to obtain the data will be blocked until it arrives.
     */
    private final AtomicReference<SoftReference<Future<T>>> reference = new AtomicReference<SoftReference<Future<T>>>();
	
    /**
     * A {@link Loader} of {@link T}.
     */
    private final Loader<T> loader;
	
    /**
     * Constructs a new reference, accepting the {@link Loader} that will lazily
     * load the data when required.
     *
     * @param loader
     *            The {@link Loader} loading the data.
     */
    public LazyLoadingReference(Loader<T> loader) {
        this.loader = loader;
    }
	
    /**
     * Returns an instance of {@link T}, lazily constructed on demand using the
     * {@link #loader Loader}.
     *
     * @return The referenced instance of {@link T}.
     * @throws InterruptedException
     *             When blocking call is interrupted.
     * @throws ExcecutionException
     *             When the {@link Loader} threw an exception trying to load the
     *             data.
     */
    public T get() throws InterruptedException, ExecutionException {
	
        // We may need to try this a couple of times
        while (true) {
	
            // Let's first get the current SoftReference
            SoftReference<Future<T>> softReference = reference.get();
	
            // And assume this SoftReference actually resolves in a Future
            boolean validSoftReference = true;
	
            // If the SoftReference is null
            if (softReference == null) {
	
                // Then we need to prepare loading the data:
                // (1) Define what needs to happen if we need to load the data
                Callable<T> eval = new Callable<T>() {
	
                    public T call() throws Exception {
                        return loader.load();
                    }
	
                };
	
                // (2) Create a FutureTask, that will eventually hold the result
                FutureTask<T> task = new FutureTask<T>(eval);
	
                // (3) Create a replacement SoftReference, pointing to the value
                // to be calculated.
                softReference = new SoftReference<Future<T>>(task);
	
                // Now try to set the AtomicReference, if it's still null
                if (validSoftReference = reference.compareAndSet(null,
                        softReference)) {
	
                    // We've set the AtomicReference, now let's make sure there
                    // is a value held by the SoftReference.
                    task.run();
	
                }
	
                // Otherwise, if the AtomicReference has been populated
                // meanwhile, we are just going to drop the FutureTask and
                // SoftReference created.
            }
	
            // In case we now have a valid SoftReference
            if (validSoftReference) {
	
                try {
	
                    // Obtain the Future
                    Future<T> future = softReference.get();
	
                    // ... and if it is not null
                    if (future != null) {
	
                        // ... we can return the value
                        return future.get();
	
                    } else {
	
                        // ... otherwise it's obviously a collected reference.
                        // In that case, we need to make sure it's getting
                        // replaced by a new SoftReference if we reenter the
                        // loop.
                        reference.compareAndSet(softReference, null);
                    }
	
                } catch (CancellationException e) {
	
                    // If Future.get throws a CancellationException, we need to
                    // set the AtomicReference to null.
                    reference.compareAndSet(softReference, null);
	
                }
            }
        }
    }
	
    /**
     * The interface to be implemented when loading instances of {@link T}.
     *
     * @param <T>
     *            The type of object to be loaded.
     */
    public interface Loader<T> {
	
        /**
         * Loads the instance of {@link T}.
         *
         * @return The instance of {@link T} loaded.
         * @throws Exception
         *             If the {@link Loader} fails to
         */
        T load() throws Exception;
	
    }
	
}

Technorati Tags: ,

Read: On how it is OK to be lazy

Topic: Indian House Cricket Previous Topic   Next Topic Topic: A second look at Dare on the OWF

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use