单例正确写法

Singleton正确写法:

今天偶然看到设计模式,就复习了一下,这里再对单例进行一个回顾和总结。

1.静态内部类

public class Singleton {

    private Singleton(){

    }

    private static class SingletonFactory{
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonFactory.instance;
    }
}

2.枚举

public enum SingtonEnum {
    INSTANCE;
    //method...
}

3.double-check

public class Singleton {
    private static Singleton singleton = null;

    private Singleton(){

    }

    public static  Singleton getInstance2(){
        if(singleton==null){
           {
                if (singleton==null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

double-check存在的问题

问题是由指令重排造成的。

对象创建的三个步骤:

1.申请内存

2.初始化

3.引用指向内存地址

指令重排后可能是1,3,2的顺序,两个线程同时访问会出现对象不为空但是没有初始化的问题,所以需要加上volatile,使用内存屏障来禁止指令重排序,来保证执行顺序。volatile保证了屏障前的一定先执行,屏障后的后执行,也就是保证对象一定是这3个操作是被一个线程执行完的,第二个线程拿到的对象是正确的。(但是这三个操作本身还是可以重排序的)

虽然理论上这种单例模式不是线程安全的,但是我测试发现得到的都是相同的示例:

/**
 * Created by wxg on 2018/8/14 17:02
 */
public class SingletonTest {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
        try {
            for (int i = 0; i < 10; i++) {
                service.submit(new Run(cyclicBarrier, "thread-" + i));
            }
        } finally {
            service.shutdown();
        }
    }
}

class Run implements Runnable {
    private CyclicBarrier cyclicBarrier;
    private String threadName;

    Run(CyclicBarrier cyclicBarrier, String threadName) {
        this.cyclicBarrier = cyclicBarrier;
        this.threadName = threadName;
    }

    @Override
    public void run() {
        try {
            cyclicBarrier.await();
            System.out.println(threadName + "-> " + Singleton.getInstance2());
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

volatile可见性和禁止重排序

可见性:强制CPU缓存失效,直接读内存。

禁止重排序:使用内存屏障实现,顺序不能跨屏障。




我的个人博客:老吴的博客 和CSDN保持同步,欢迎关注

posted @ 2018-08-14 20:20  SEC.VIP_网络安全服务  阅读(121)  评论(0编辑  收藏  举报