首页 > 代码库 > 单例与多线程

单例与多线程

一。饿汉模式

 

public class Singleton{          private static Singleton instance = new Singleton();             private Singleton(){                           }                     private static Singleton getInstance(){                 return instance;              }         } 

  第一次加载类时就会创建Singleton 实例,所以是线程安全的。另一方面,如果这个Singleton 实例的创建非常消耗系统资源,

而应用始终都没有使用Singleton 实例,那么创建Singleton 消耗的系统资源就被白白浪费了。

 

二。饱汉模式

public class Singleton{     private static Singleton instance = null;     private Singleton(){     }     public static Singleton getInstance(){            if (instance == null){                 instance = new Singleton();            }           return instance;    }    }

  线程不安全:两个线程A 和B 同时进入该方法的情形
1. A 进入if 判断,此时foo 为null,因此进入if 语句
2. B 进入if 判断,此时A 还没有创建foo,因此foo 也为null,因此B 也进入if 语句
3. A 创建了一个Foo 并返回
4. B 也创建了一个Foo 并返回

 

三。解决方法

1.同步方法

 public static synchronized Singleton getInstance(){         if (instance == null){                instance = new Singleton();          }         return instance; }

  这种解决办法的确可以防止错误的出现,但是它却很影响性能:每次调用getInstance 方法的时候都必须获得
Singleton 的锁,而实际上,当单例实例被创建以后,其后的请求没有必要再使用互斥机制了

 

2.同步块

           public static Singleton getInstance(){          if(single == null){                 synchronized (Singleton.class) {    //保证了同一时间只能只能有一个对象访问此同步块                        if(single == null){                          single = new Singleton();                           }                 }        }          return single;       }   

  上述描述似乎已经解决了我们面临的所有问题,但从JVM 的角度讲,这些代码仍然可能发生错误。对于JVM 而言,它执行的是一个个Java 指令。
在Java 指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton()语句是分两步执行的。但是JVM 并不保证这两个操作的
先后顺序,也就是说有可能JVM 会为新的Singleton 实例分配空间,然后直接赋值给instance 成员,然后再去初始化这个Singleton 实例。这样就
可能出错,我们仍然以A、B 两个线程为例:
1. A、B 线程同时进入了第一个if 判断
2. A 首先进入synchronized 块,由于instance 为null,所以它执行instance = new Singleton();
3. 由于JVM 内部的优化机制,JVM 先画出了一些分配给Singleton 实例的空白内存,并赋值给instance成员(注意此时JVM 没有开始初始化这个实例)
然后A 离开了synchronized 块。
4. B 进入synchronized 块,由于instance 此时不是null,因此它马上离开了synchronized 块并将结果返回给调用该方法的程序。
5. 此时B 线程打算使用Singleton 实例,却发现它没有被初始化,于是错误发生了。

 

3.内部类

public class Singleton {                   private Singleton(){                         }                   private class SingletonHoledr(){                private static Singleton instance = new Singleton();             }                   private static Singleton getInstance(){                return SingletonHoledr.instance;             }        }