懒惰评估

最近,我正在编写log4j附加程序,并希望在自定义附加程序创建过程中使用logger记录一些诊断详细信息,但是log4j初始化仅在创建附加程序实例后才完成,因此该阶段记录的消息将被忽略。

我感到需要在自定义附加程序中进行延迟初始化,并开始研究选项。 在此博客中,我将分享我尝试过的事情。

我想到的一件事是Singleton方法,但是现在已知的事实是Singleton会导致测试问题,并且无法扩展它,因此混合并发和对象构造的方法并不是那么好。

如果需要单例,那么最好使用依赖注入框架而不是破坏应用程序代码。 让我们回到延迟初始化/评估。

一些编程语言(例如scala / swift等)支持惰性,因此不需要自定义代码即可执行此操作,但是在Java空间中,我们仍然必须编写线程安全的代码才能正确执行。

让我们看一下Java中的一些选项以及获得的性能类型。

–使用同步的蛮力

这是最简单,效率最低的一种,scala正在使用这种方法。 Scala一个可用@ 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);
        }
    }


}

–双锁

编写起来并不复杂,并且具有良好的性能。

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);
        }
    }


}

–使用未来任务

这种方法易于编写,并具有良好的性能。

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);
        }
    }
}

双锁方法可提供最佳性能,而蛮力则是最差的。 我使用不同数量的线程对100万次调用进行了快速基准测试。

三个都
单锁性能非常差,让我们通过删除单锁来查看数字,以了解“双锁和未来任务”的执行情况。

2种

这些基准测试很快完成,但是详细的基准测试数字应该接近。

可以在github上获得此博客文章的代码

翻译自: https://www.javacodegeeks.com/2016/08/lazy-evaluation.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值