使用Java 8 Performance的经典单例与Lazy
最近我读了一篇文章“
Be Lazy With Java 8”,它引入了一种创建惰性对象的方法(在第一次访问时将创建其内部状态的对象).
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; } } 我发现这个模式与众所周知的单例模式非常相似,除了泛型: 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成员来同步对内部对象的访问,而单例模式实现很少(我个人更喜欢将它与具有一个静态最终成员的内部助手类一起使用).我假设第二个模式具有更好的性能,因为每个对Lazy对象的getOrCompute方法的调用都涉及从主存储器中读取(由于volatile成员),而Singleton由L1和amp;中缓存的类加载器加载一次. L2内存缓存. 以下是结果表: 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()); } 解决方法
我不是并发专家,但似乎你的Lazy初始化程序不正确.在基准测试中,您使用Scope.Thread状态.但这意味着每个线程都拥有自己的Lazy,因此没有真正的并发性.
我用Lazy编写了自己的基准测试(基于apache commons LazyInitializer),Eager和静态内部类. 急于 import java.util.Properties; public class Eager { private final Properties value = new Properties(); public Properties get(){ return value; } } 懒 import org.apache.commons.lang3.concurrent.ConcurrentException; import org.apache.commons.lang3.concurrent.LazyInitializer; import java.util.Properties; public class Lazy extends LazyInitializer<Properties> { @Override protected Properties initialize() throws ConcurrentException { return new Properties(); } } PropertiesSingleton和你的一样. 基准 import org.apache.commons.lang3.concurrent.ConcurrentException; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import java.util.Properties; @State(Scope.Benchmark) public class MyBenchmark { private Lazy lazyProperties = new Lazy(); private Eager eagerProperties = new Eager(); @Benchmark public Properties testEager(){ return eagerProperties.get(); } @Benchmark public Properties testLazy() throws ConcurrentException { return lazyProperties.get(); } @Benchmark public Properties testSingleton(){ return PropertiesSingleton.getProperties(); } } 结果 Benchmark Mode Cnt Score Error Units MyBenchmark.testEager thrpt 20 90980753,160 ± 4075331,777 ops/s MyBenchmark.testLazy thrpt 20 83876826,598 ± 3445507,139 ops/s MyBenchmark.testSingleton thrpt 20 82260350,608 ± 3524764,266 ops/s (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |