The Artima Developer Community
Sponsored Link

Java Buzz Forum
Lazy Loading Caching References

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
Lazy Loading Caching References Posted: Jul 29, 2008 1:17 PM
Reply to this message Reply

This post originated from an RSS feed registered with Java Buzz by Wilfred Springer.
Original Post: Lazy Loading Caching References
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

… are not easy to implement.

During the last couple of days, I’ve been thinking about it a little. What I wanted is to have a reference to an object that would only load the actual object from a ByteBuffer on the first attempt to use that object. And I wanted to make sure that the reference would - from that point on - hold on to that object. But since I expected a *huge* number of these references, I needed to make sure that they could be garbage collected, if required.

Implementing lazy loading is easy - that is, if you ignore concurrency. The nasty problem with concurrency is that you will always have multiple threads competing to create the first instance of the object that needs to be loaded. And if lazy loading is important, you want to prevent threads creating those objects as much as possible.

The old school Double Check Locking solution does work, as of Java 1.5, if you implement it carefully. However, in a real caching solution, you want to be able to evict objects from your cache, if memory constraints require you to do so. So instead of having a volatile reference to the object you are caching, you want a weak or soft reference. But at the same time, you want atomic update semantics. Atomic or soft. Soft or atomic. Unfortunately, Java doesn’t come with a blended version.

After digging around for an hour yesterday, I finally implemented something myself. I think it might just work. ;-)

The trick here is the Future. The Future allows me to defer construction of the object until I really need it. And the atomic update mechanism of AtomicReference makes sure I can inject a Future if some other thread has not yet done it before me. By running the future after I have injected it into the AtomicReference, I have basically made sure all other threads are waiting for the same Future to complete, and by making sure the Future returns a SoftReference, I make sure that the garbage collector gets a chance to evict the referenced object from memory.

I am now trying to figure out how to create a unit test for this class. It may be challening, but I have used MultithreadedTC before for this purpose, so once I’ve swapped everything I knew about that back in, I will give it a try.

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 reference to an instance of T that will be loaded by the {@link Loader}
 * passed in, on demand. The reference will also release its reference to the
 * instance created by the {@link Loader} if the VM needs more memory.
 *
 * @author Wilfred Springer
 *
 * @param <T>
 *            The type of object referenced.
 */
public class LazyLoadingReference<T> {
	
    /**
     * An {@link AtomicReference} to a {@link Future} of a {@link SoftReference}
     * to an instance of <code>T</code>. 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<Future<SoftReference<T>>> reference = new AtomicReference<Future<SoftReference<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
     */
    public T get() throws InterruptedException {
        while (true) {
            Future<SoftReference<T>> future = reference.get();
            if (future == null) {
                Callable<SoftReference<T>> eval = new Callable<SoftReference<T>>() {
	
                    public SoftReference<T> call() throws Exception {
                        return new SoftReference<T>(loader.load());
                    }
	
                };
                FutureTask<SoftReference<T>> task = new FutureTask<SoftReference<T>>(
                        eval);
                if (reference.compareAndSet(null, task)) {
                    future = task;
                    task.run();
                }
            }
            try {
                return future.get().get();
            } catch (CancellationException e) {
                reference.set(null);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }
	
    /**
     * 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.
         */
        T load();
	
    }
	
}

Read: Lazy Loading Caching References

Topic: A story about "Planning Extreme Programming" from AllConsuming.net Previous Topic   Next Topic Topic: Mailmerge MS-Word template using OpenOffice and Java

Sponsored Links



Google
  Web Artima.com   

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