在《java并发编程实战》这本书的第十六章中讲到不安全的发布时,给了一个不安全的延迟初始化

示例:

public class UnsafeLazyInitialization {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null)
            resource = new Resource(); // unsafe publication
        return resource;
    }

    static class Resource {
    }
}

一般情况下,估计大家都会这么写代码,在非并发环境中,这个getInstance方法会工作的很好,但是放到并发环境中,问题就来了,比如竞态条件,但这里还存在另外一个问题,即另一个线程可能会看到对部分构造的Resource实例的引用。

简单点来说,就是线程A在访问getInstance方法时,发现resource为null,于是就resource设置为一个新实例,在这个过程中,线程B也调用getInstance方法,发现resource不为空,因此就直接使用这个resource实例了。看起来貌似没有问题,但是因为线程A实例化resource的操作和线程B读取resource实例的操作之间不存在Happens-Before关系,所以,在线程B使用resource实例时,resource实例也许还未构造完成,这就导致了线程B看到的resource实例不正确的状态。

解决这个问题的一个简单办法就是使用同步,这也是我们经常会使用的办法:

public class SafeLazyInitialization {
    private static Resource resource;

    public synchronized static Resource getInstance() {
        if (resource == null)
            resource = new Resource();
        return resource;
    }

    static class Resource {
    }
}

下面再来看看另一种解决办法,书中称之为延迟初始化占位类模式,我认为这个方法很巧妙:

public class ResourceFactory {
    private static class ResourceHolder {
        public static Resource resource = new Resource();
    }

    public static Resource getResource() {
        return ResourceFactory.ResourceHolder.resource;
    }

    static class Resource {
    }
}

 

posted @ 2022-11-27 12:20  活捉老郭  阅读(28)  评论(0)    收藏  举报