How to implement thread-safe lazy initialization?

2019-01-07 06:52发布

What are some recommended approaches to achieving thread-safe lazy initialization? For instance,

// Not thread-safe
public Foo getInstance(){
    if(INSTANCE == null){
        INSTANCE = new Foo();
    }

    return INSTANCE;
}

11条回答
Rolldiameter
2楼-- · 2019-01-07 06:54

If you're using Apache Commons Lang, then you can use one of the variations of ConcurrentInitializer like LazyInitializer.

Example:

lazyInitializer = new LazyInitializer<Foo>() {

        @Override
        protected Foo initialize() throws ConcurrentException {
            return new Foo();
        }
    };

You can now safely get Foo (gets initialized only once):

Foo instance = lazyInitializer.get();

If you're using Google's Guava:

Supplier<Foo> fooSupplier = Suppliers.memoize(new Supplier<Foo>() {
    public Foo get() {
        return new Foo();
    }
});

Then call it by Foo f = fooSupplier.get();

From Suppliers.memoize javadoc:

Returns a supplier which caches the instance retrieved during the first call to get() and returns that value on subsequent calls to get(). The returned supplier is thread-safe. The delegate's get() method will be invoked at most once. If delegate is an instance created by an earlier call to memoize, it is returned directly.

查看更多
▲ chillily
3楼-- · 2019-01-07 06:54

Put the code in a synchronized block with some suitable lock. There are some other highly specialist techniques, but I'd suggest avoiding those unless absolutely necessary.

Also you've used SHOUTY case, which tends to indicate a static but an instance method. If it is really static, I suggest you make sure it isn't in any way mutable. If it's just an expensive to create static immutable, then class loading is lazy anyway. You may want to move it to a different (possibly nested) class to delay creation to the absolute last possible moment.

查看更多
不美不萌又怎样
4楼-- · 2019-01-07 06:57
class Foo {
  private volatile Helper helper = null;
  public Helper getHelper() {
    if (helper == null) {
      synchronized(this) {
        if (helper == null) {
          helper = new Helper();
        }
      }
    }
  return helper;
}

This is called double checking! Check this http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html

查看更多
Luminary・发光体
5楼-- · 2019-01-07 06:58

For singletons there is an elegant solution by delegating the task to the JVM code for static initialization.

public class Something {
    private Something() {
    }

    private static class LazyHolder {
            public static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
            return LazyHolder.INSTANCE;
    }
}

see

http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

and this blog post of Crazy Bob Lee

http://blog.crazybob.org/2007/01/lazy-loading-singletons.html

查看更多
做自己的国王
6楼-- · 2019-01-07 06:58

The easiest way is to use a static inner holder class :

public class Singleton {

    private Singleton() {
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
查看更多
叛逆
7楼-- · 2019-01-07 06:59

If you use lombok in your project, you can use a feature described here.

You just create a field, annotate it with @Getter(lazy=true) and add initialization, like this: @Getter(lazy=true) private final Foo instance = new Foo();

You'll have to reference field only with getter (see notes in lombok docs), but in most cases that's what we need.

查看更多
登录 后发表回答