I recently read the article " " Lazy With Java 8 ", which introduced a way to create lazy objects (objects that will create their internal state on first access).
public final class Lazy<T> {
private volatile T value;
public T getOrCompute(Supplier<T> supplier){
final T result = value;
return result == null ? maybeCompute(supplier) : result;
}
private synchronized T maybeCompute(Supplier<T> supplier) {
if (value == null){
value = Objects.requireNonNull(supplier.get());
}
return value;
}
}
I found that this pattern is very similar to the well-known singleton pattern, with the exception of generics:
public class PropertiesSingleton {
public static Properties getProperties(){
return Helper.INSTANCE;
}
private final static class Helper{
private final static Properties INSTANCE = computeWithClassLoaderLock();
private static Properties computeWithClassLoaderLock(){
return new Properties();
}
}
}
Lazy volatile , ( , ). , , getOrCompute Lazy (- ), Singleton , L1 L2.
JMH, CentOS 6 Intel (R) Core i5-3470 @3.20 GHz. Git: https://github.com/maximkir/LazyObjectVsSingletonPerformance
:
Benchmark Mode Cnt Score Error Units
LazyVsSingletonPerformance.testLazy sample 1101716 33.793 ± 0.148 ns/op
LazyVsSingletonPerformance.testSingleton sample 622603 33.993 ± 0.179 ns/op
, , . , . ? ? ? ?
:
@State(Scope.Thread)
public class LazyVsSingletonPerformance {
Blackhole bh = new Blackhole();
Lazy<Properties> lazyProperties = new Lazy<>();
public static void main(String... args) throws Exception{
Options opts = new OptionsBuilder()
.include(LazyVsSingletonPerformance.class.getSimpleName())
.warmupIterations(3)
.forks(2)
.measurementIterations(3)
.mode(Mode.SampleTime)
.measurementTime(TimeValue.seconds(10))
.timeUnit(TimeUnit.NANOSECONDS)
.build();
new Runner(opts).run();
}
@Benchmark
public void testLazy(){
bh.consume(lazyProperties.getOrCompute(() -> new Properties()));
}
@Benchmark
public void testSingleton(){
bh.consume(PropertiesSingleton.getProperties());
}