双重校验锁实现对象单例(线程安全)

单例模式在多线程下实现线程安全:

public class Singleton { 
    private volatile static Singleton uniqueInstance; 
    private Singleton() { }
    public static Singleton getUniqueInstance() { 
        //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) { 
        //类对象加锁 
            synchronized (Singleton.class) { 
                if (uniqueInstance == null) { 
                    uniqueInstance = new Singleton(); 
                } 
            } 
        }
        return uniqueInstance; 
    } 
}

一、synchronized关键字的使用方式:

        1、修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
        2、修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 。也就是给当前类加锁,会作
用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态
资源,不管 new 了多少个对象,只有一份,所以对该类的所有对象都加了锁)。所以如果一个线程 A 调用一个实
例对象的非静态 synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允
许的,不会发生互斥现象, 因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态
synchronized 方法占用的锁是当前实例对象锁
        3、修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 synchronized
法一样, synchronized(this) 代码块也是锁定当前对象的。 synchronized 关键字加到 static 静态方法和
synchronized(class) 代码块上都是是给 Class 类上锁。这里再提一下: synchronized 关键字加到非 static 静态
方法上是给对象实例上锁。另外需要注意的是:尽量不要使用 synchronized(String a) 因为 JVM 中,字符串常量
池具有缓冲功能!
 
二、此例中volatile关键字的作用:
        uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分
为三步执行:
        1. uniqueInstance 分配内存空间
        2. 初始化 uniqueInstance
        3. uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2 。指令重排在单线程环境下不会出先问题,但是在
多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 3 ,此时 T2 调用
getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance ,但此时 uniqueInstance 还未被
初始化。
       使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
 
延申: synchronized 关键字和 volatile 关键字比较
        1、volatile 关键字 是线程同步的 轻量级实现 ,所以 volatile 性能肯定比 synchronized 关键字要好 。但是 volatile
键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 synchronized 关键字在 JavaSE1.6 之后进
行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行
效率有了显著提升, 实际开发中使用 synchronized 关键字的场景还是更多一些
        2、多线程访问volatile 关键字不会发生阻塞,而 synchronized 关键字可能会发生阻塞
        3、volatile关键字能保证数据的可见性,但不能保证数据的原子性。 synchronized 关键字两者都能保证。
        4、volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访
问资源的同步性。
 

你可能感兴趣的:(双重校验锁实现对象单例(线程安全))