之前在论坛看到讲解单利模式http://www.cnblogs.com/geek6/articles/3951677.htmljava] view plaincopy
public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:a>A、B线程同时进入了第一个if判断b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了
。这里头”new singleton没实例化就出块”这句话有点迷
public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:a>A、B线程同时进入了第一个if判断b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了
。这里头”new singleton没实例化就出块”这句话有点迷
因为a线程在执行 instance = new Singleton(); 会分配个instance存储地址和初始化2个操作,jvm的优化机制会先分配instance一个内存地址,此时instance不等于null,a线程语句结束完成同步块后,b线程可以进入同步块,此时instance不等于null,但此时instance = new Singleton()由于jvm优化机制中产生异步,a线程中instance初始化还在进行中。 所以b线程因instance非空而跳过了instance初始化语句然后出了同步块,但实际上instance初始化并未完成,这时候如果b线程有使用instance的语句的话会因为初始化还未完成而报错,这里解说的很清楚没什么问题吧?!
public class Main { static class Test{
public Test(){
System.out.println("构造函数");
}
}
public static void main(String[] args) throws Exception {
Test t = new Test();
System.out.println("构造返回");
}
}
这段代码的问题不是这个,问题是在synchronized (instance) 这一句,instance是null的话是无法做同步的,如果锁一个非null的对象,这个代码是可以正常运行。但是单例最简单也最方便的,也是我经常用的(他后面的例子还是有点复杂):
class Singleton{
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}