概述

在单例模式的懒汉模式中,getInstance方法通常是采用如下方式写的:

public static STest getInstance(){
    if(sTest==null){
        sTest = new STest();
    }
    return sTest;
}

这样的写法在多线程的情况下有可能造成不同的线程都实例化了一个对象出来,所以该写法是线程不安全的。那么怎么办呢?

加锁的解决方法

首先,自然想到既然线程不安全的原因是多个线程可能同时执行方法,那么加锁肯定是一个不错的解决方案,

public static synchronized STest getInstance2(){
    if(sTest==null){
        sTest = new STest();
    }
    return sTest;
}

这样在一定程度上的确能保证多线程下的安全,但是如果多个线程都要调用该方法,然后都要等待排在前面的线程去判断sTest对象是否为空,在效率上落了下乘。所以为了兼顾安全和效率,有了双重判否的解决方式。

双重判否

双重判否并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

全部代码

package Test0328;

/**
 * Created by Cesar on 2016/3/28.
 */
public class STest {
    private static volatile STest sTest;

    private STest() {
    }

    /**
     * 这是普通模式
     *
     * @return
     */
    public static STest getInstance() {
        if (sTest == null) {
            sTest = new STest();
        }
        return sTest;
    }

    /**
     * 这是同步模式
     *
     * @return
     */
    public static synchronized STest getInstance2() {
        if (sTest == null) {
            sTest = new STest();
        }
        return sTest;
    }

    /**
     * 这是双重判否模式
     *
     * @return
     */
    public static STest getInstance1() {
        if (sTest == null) {
            synchronized (STest.class) {
                if (sTest == null) {
                    sTest = new STest();
                }
            }
        }
        return sTest;
    }
}

Cesar
277 声望10 粉丝

Talk is cheap