参考几位高人的文章,然后结合自己的理解,在此做一个小小的总结:
http://www.iteye.com/topic/537563
http://www.iteye.com/topic/260515
http://www.iteye.com/topic/344876
在多线程环境下的单例模式:
1. 使用(Double-Checking-Lock)双检锁:
public class Singleleton {
private Singleleton() {}
private static Singleleton instance = null;
public static Singleleton getInstance() {
if(instance == null){
synchronize(Singleleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
“双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因。”
(http://www.ibm.com/developerworks/cn/java/j-dcl.html#2)
DCL被破坏的原因在于:
两个线程可能最终得到的instance引用是一样的,但是却有可能访问到instance的成员变量的不正确值(因为instance的引用值在其真正的构造函数执行之前被返回,或者称之为:被泄露)
针对instance =new Singleton(),其对应的伪代码可能为:
1. mem = allocate();//Allocate memory for Singleton object.
2. instance = mem; //Note that instance is now non-null, but has not been initialized.
3. ctorSingleton(instance); //Invoke constructor for Singleton passing instance.
其中第二句和第三句的执行顺序是不确定的(因为有reorder的存在),而这点正是引发问题的关键.
要想避免reorder,可以使用关键字volatile(前提是JVM很好地实现了volatile的顺序一致性):
被volatile修饰的变量A,系统不允许对A的写操作和之前的读写操作(这里的"读写"包括任何变量的读写,不只是针对A)进行优化重排执行(reorder),同时,不会对读操作和之后的读写操作进行优化重排执行:
<Java Concurrency in Practise>: the system will not allow a write of a volatile to be reordered with respect to any previous read or write, and a read of a volatile cannot be reordered with respect to any following read or write.
这样,volatile保证了3肯定先执行,然后再执行2.
使用Happen-Before(HB)规则可以更好的理解volatile的作用:
int volatile count;
int number;
....
1. int a = count;
2. int b = number;
因为count被声明为volatile,语句1在读取count的时候应该读取最近一次对count的写入值,因此最近一次针对count的写操作W1(count)和当前的读操作R1(count)两者之间的顺序是不可以被优化调整的,即必有W1(count) -HB-> R1(count)
如果在代码中对number的写总是发生在对count写之前,即有关系W1(number) -HB-> W1(count) -HB-> R1(count),再根据语句顺序有R1(count)-HB->R1(number)
根据HB的传递性,我们可以得到W1(number) -HB-> R1(number),也即:在取number的时候应该读取最近一次对number的写入.
这里虽然number并未被声明为volatile,但由于语句1的存在,保证了number的可见性.即使变量a在下面的语境中从未被引用,编译器也不会在优化时把语句1去掉(因为volatile的存在)
如果想了解更具体的语境,可以参考CurrentHashMap中关于count和modCount的使用,这里有一篇文章,讲得很好:
http://www.iteye.com/topic/344876
2. 使用static来实现
public class Singleleton {
private static class SingleletonHolder{
private static Singleleton instance = new Singleleton();
}
public static Singleleton getInstance(){
return SingleletonHolder.instance ;
}
}
分享到:
相关推荐
这种写法可以保证线程安全.两个if都是不能去掉的.如果去掉第一个if: 那么所有的线程都会到这里来先获取锁,然后判断singleton是否为空.所有线程都会串行
在懒汉式基础上利用synchronize关键字和volatile关键字确保第一次创建时没有线程间竞争而产生多个实例,仅第一次创建时同步,性能相对较高 登记式。作为创建类的全局属性存在,创建类被装载时创建 枚举。java中枚举...
详细的讲述了多线程的各种用法 Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠...
Java 线程系列博文总结...Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器
面试官:有用过单例模式吗? 我:有有有(自信满满)。 面试官:说说单例模式几种写法? 我:懒汉式和饿汉式,懒汉式巴拉巴拉,饿汉式巴拉巴拉。 面试官:我们都知道synchronized加锁是比较耗费资源的,你这种写法...
懒汉式优化-加锁同步3.DCL双检锁/双重校验锁重排序问题多线程执行时序表volatile 作用优化-基于volatile 的双重检查锁4.IODH按需初始化持有者反射问题私有构造函数异常处理5.枚举实现单例使用推荐 什么是单例? 单例...
主要讲述java线程volatile关键字
C++基本功:全面掌握const、volatile和mutable关键字
Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释 3 四、线程的状态转换和生命周期 4 Java线程:创建与启动 7 Java线程:线程名称的设定及获取 10 Java...
Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结
1. Java多线程学习(一)Java多线程入门 2. Java多线程学习(二)synchronized...7. Java多线程学习(六)Lock锁的使用 8. Java多线程学习(七)并发编程中一些问题 9. Java多线程学习(八)线程池与Executor 框架
07 使用JAVA建立稳定的多线程服务器 (3) 08 线程池的介绍及简单实现 09 Java实时多任务调度过程中的安全监控设计 (3) 10 不要重新分配被锁定对象的对象引用 11 以全局的固定顺序获取多个锁来避免死锁 12 Java...
Java线程(二):线程同步synchronized和volatile 详细讲解Java 同步的原理技术资料
volatile是C#中用于控制同步的关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,volatile是修饰变量的修饰符。 1、volatile的使用...
主要介绍了Linux C中多线程与volatile变量的相关资料,需要的朋友可以参考下
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。 由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此...
正常情况下,如果我们不使⽤volatile,那么每条线程都会有⾃⼰的缓存,当全局变量被修改时,其 17 他线程可能并不会被通知到。 ● volatile并不能真正的保证线程安全。它只能确保⼀个线程修改了数据后,其他线程能够...
volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,下面看一下为什么要慎重使用volatile关键字