Home » Java » Enterprise Java » Lazy evaluation

About Ashkrit Sharma

Ashkrit Sharma
Pragmatic software developer who loves practice that makes software development fun and likes to develop high performance & low latency system.

Lazy evaluation

Recently i was writing log4j appender and wanted to use logger in it to log some diagnostic details during custom appender creation, but log4j initialization completes only after appender instance are created, so message logged during this phase are ignored.

I felt the need for lazy initialization in custom appender and started to look at options. In this blog i will share things that i tried.

One of the thing that came to my mind was Singleton approach but now it is known fact that singleton causes problem with testing and make it impossible to extend it, so approach of mixing concurrency & object construction is not that good.

Incase if singleton is required then it is better to use Dependency Injection framework rather than spoiling your application code. Lets get back to lazy initialization/eval.

Some programming language like scala/swift etc has support for lazy, so no custom code is required to do this but in java space we still have to write thread safe code to get it right.

Lets look at some options we have in java and what type of performance we get.

– Brute force using Synchronized

This is the most simple and inefficient one, scala is using this approach. Scala one is available @ScalaLazy.java

public class SingleLock<V> implements Lazy<V> {

    private Callable<V> codeBlock;
    private V value;

    public SingleLock(Callable<V> codeBlock) {
        this.codeBlock = codeBlock;
    }

    @Override
    public synchronized V get() {
        if (value == null) {
            setValue();
        }
        return value;
    }

    private void setValue() {
        try {
            value = codeBlock.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}

– Double lock

This is little complex to write and gives good performance.

public class DoubleLock<V> implements Lazy<V> {

    private Callable<V> codeBlock;
    private V value;
    private volatile boolean loaded;

    public DoubleLock(Callable<V> codeBlock) {
        this.codeBlock = codeBlock;
    }

    @Override
    public V get() {
        if (!loaded) {
            synchronized (this) {
                if (!loaded) {
                    setValue();
                    loaded = true;
                }
            }
        }
        return value;
    }

    private void setValue() {
        try {
            value = codeBlock.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}

– Using Future task

This approach is simple to write and gives good performance.

public class LazyFutureTask<V> implements Lazy<V> {

    private final FutureTask<V> futureTask;

    public LazyFutureTask(Callable<V> codeBlock) {
        this.futureTask = new FutureTask<>(codeBlock);
    }

    @Override
    public V get() {
        futureTask.run();
        return getValue();
    }

    private V getValue() {
        try {
            return futureTask.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Double lock approach gives the best performance and brute force one is worst. I did quick bench mark for 1 Million calls using different number of thread.

All three
Single lock performance is very bad, lets have look at the number by removing single lock to see how Double Lock & Future Task performed.

2 Types

 

These benchmark are done very quickly but detailed benchmark numbers should be close.

Code for this blog post is available @ github

Reference: Lazy evaluation from our JCG partner Ashkrit Sharma at the Are you ready blog.
(0 rating, 0 votes)
You need to be a registered member to rate this.
2 Comments Views Tweet it!
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

2
Leave a Reply

avatar
1 Comment threads
1 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Ashkrit SharmaRobert Cooper Recent comment authors

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
Robert Cooper
Guest

What, exactly, is it you are evaluating here? It is just time through the method *after* initialization? Are you looking for something that evaluates the lazy creation the fastest? I would be curious how AtomicBoolean initted …. get() { if(!innitted.getAndSet(true){ setValue() } return value; } Would perform. Generally I suspect on x64 systems where CAS is a CPU level op, it might work better than a volatile memory access. If you are in a situation where you are creating a LOT of lazies and only accessing a few of them, it is definitely going to be better than the double… Read more »

Ashkrit Sharma
Guest

Problem that i am trying to solve is

– Thread safe initialization
– Waiting for other threads while initialization is in progress.

Things can be made worked with help of AtomicBoolean but some extra work will be required to manage waiting thread and will endup writing something similar to FutureTask.

Synchnorization/FutureTask has built in support for waiting.