Java 线程安全的单例模式

单例模式在Java中是一种使用非常普遍的设计模式。它可以保证一个对象在整个环境中只有一个实例。单例模式实现方式简单来说就是
1:判断当前实例是否为null
2:如果为null则新建一个对象,并返回;否则,直接返回该对象。
从上面来看,单例模式生成对象的方式并不是原子性的,因为涉及到读取、判断、实例化三个过程。所以在多线程的场景下,很难保证我们最终只建立一个实例。所以下面我自己总结了几种线程安全的单例模式实现方式。

第一种,通过synchronized 关键字双重非空判断

public class ThreadSafeSingleton {
    //volatile的作用是防止指令重排序。
    //对象实例化并不是一个原子操作,那么其实会存在这种情况,对象还没有创建完毕,非空判断if(threadSafeSingleton == null)的结果就已经为false了。正常我们的理解是,当对象完全创建完毕之后,threadSafeSingleton才不为null。但是底层虚拟机实现可能并不是这样的。volatile是可以防止指令重排序的。
    private static volatile ThreadSafeSingleton threadSafeSingleton;

    public ThreadSafeSingleton getInstance(){
        if(threadSafeSingleton == null){
            synchronized (ThreadSafeSingleton.class) {
                //注意,这个非空判断是容易忽略的地方
                //这个判断的作用是针对以下的场景
                //1:线程1获取到锁,并且正在创建对象
                //2:线程2通过了第一个非空判断,正在等待锁资源。这个时候线程1完成了对象创建,准备释放锁
                //3:线程2获得到了锁,然后进行第二个非空判断,发现这个对象已经创建好了,那么则不需要再次创建对象。如果没有这个非空判断,那么有可能线程2会再次创建一个对象
                if(threadSafeSingleton == null){
                    threadSafeSingleton = new ThreadSafeSingleton();
                }
            }
        }
        return threadSafeSingleton;
    }
}

第二种,在类加载的时候就把对象给初始化。适合对象结构和初始化过程比较简单的那种

public class HungarySingleton {
    //如果实例化的过程比较复杂,比如依赖一些外部的配置文件等,可以使用静态块的方式,方便我们做一些个性化的创建对象
    private static HungarySingleton hungarySingleton = new HungarySingleton();

    private HungarySingleton(){

    }

    public HungarySingleton getInstance(){
        return hungarySingleton;
    }
}

其实还有种性能最差的单例模式设计方式,就是给getInstance方法加上synchronized 关键字,因为每次调用方法都需要获取锁资源,所以方法同一时刻只能够被一个线程调用。全部代码我就不贴了,简单看下getInstance方法的定义就可以了解了:

public static synchronized Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
}  
阅读更多
个人分类: java 系统 设计模式
所属专栏: Java 相关
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭