先说下单例模式 在java中的体现
饿汉模式
public class Test1 {
public static Object object = new Object();
private Test1(){}
public static Object getObject(){
return object;
}
}
懒汉模式
public class Test2 {
private static Object object = null;
private Test2(){}
private static Object getObject(){
if(object == null){
object=new Object();
}
return object;
}
}
通常我们接触到的单例模式上的体现也就这两种了 ,那么他们在多线程中会有什么样的体现呢?
第一种饿汉模式在多线程中是线程安全的,但是启动时间会变慢
第二种懒汉模式在多线程中是不安全的,无锁则造成了可能有多个线程同时进入到if里面去
那么在多线程中我们应该如何使用呢?下面推荐一种方案,也是比较实用的方案
public class Test3 {
public static class Test{
public static Object object = new Object();
private Test(){}
public static Object getObject(){
return object;
}
}
public static Object getObject(){
return Test.getObject();
}
}
这种方案利用static 关键字的特点 只用在调用Test3.getObject();的时候才会去初始化构建Test 所以这里做到了启动时间快线程也安全
另外提一下 多线程下懒汉模式的体现
public class Test4 {
public static Object object = null;
public static synchronized Object getObject(){
if(object==null){
object = new Object();
}
return object;
}
}
加锁,所以线程安全了,但是这个锁的粒度有点大,还可以优化一下
我们来看一下双重验证锁,双重验证锁减小了锁的粒度
public class Test5 {
public static Object object = null;
public static Object getObject(){
if(object==null){
synchronized (Test5.class) {
if(object==null){
object=new Object();
}
}
}
return object;
}
}
这个双重验证锁的方式看上去是一个线程安全的,但是博主从一些文章中了解到这个双重验证锁的方式其实也并非线程安全的
这里引用一下从其他博文中拔来的一段话
原文http://www.iteye.com/topic/537563
但是二次检查自身会存在比较隐蔽的问题,查了Peter Haggar在DeveloperWorks上的一篇文章,对二次检查的解释非常的详细:
其实找到这篇文章之后,我的问题基本上就已经可以解决了,但是看到回帖的同学们也有一些和我一样的问题,还想把这个问题继续梳理一遍。
使用二次检查的方法也不是完全安全的,原因是 java 平台内存模型中允许所谓的“无序写入”会导致二次检查失败,所以使用二次检查的想法也行不通了。
Peter Haggar在最后提出这样的观点:“无论以何种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。”
所以最后总结,在多线程中的单例模式我们尽量采用第一种推荐的方式就好!