单例模式中懒汉模式的非线程安全问题的解决方法 package singleton1; //饿汉模式(立即加载) public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton
          单例模式中懒汉模式的非线程安全问题的解决方法
 
package singleton1;
//饿汉模式(立即加载)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
 
 
而懒汉模式也叫延迟加载,即在get时才会被创建实例。懒汉模式在多线程的环境中就会出现多实例的情况,与单例模式相背离。如下代码可证:
 
package singleton2;
//懒汉模式
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
try {
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}
  创建线程: 
package singleton2;
public class ThreadA extends Thread{
@Override
public void run(){
System.out.println(Singleton.getInstance().hashCode());//根据实例对象哈希值是否相同来判断对象是否相同
}
}
 
 创建运行类: 
package singleton2;
public class Run {
public static void main(String[] args) {
ThreadA t1=new ThreadA();
ThreadA t2=new ThreadA();
ThreadA t3=new ThreadA();
t1.start();
t2.start();
t3.start();
}
}
 运行结果:
  
1321522194
2134502363
866891806
  哈希值不同,所以对象不同,即不是单例模式。如何解决这个问题呢,下面提供了几种方法: 
 
 
一、方法上声明synchronized关键字(效率低)
 
package singleton2;
//懒汉模式
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
//1 方法上声明synchronized关键字
synchronized public static Singleton getInstance(){
try {
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}
 
 
运行结果如下:
 
1876189785
1876189785
1876189785
   
 
二、同步代码块(效率低)
 
package singleton3;
//懒汉模式
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
try {
// 2 使用同步代码块,但效率低
synchronized(Singleton.class){
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}
 
 
运行结果:
 
1619327594
1619327594
1619327594
        使用同步代码块方式也能解决上述问题,虽然不需要判断实例是否为null,但同样的也会带来效率低的毛病。 
 
三、使用DCL(Double-Check Locking)双检查锁机制---(推荐)
 
public class Singleton {
  private static volatile Singleton instance = null;
  private Singleton(){}
  
  public static Singleton getInstance(){
    if(instance == null){
      synchronized(Singleton.class){
        if(instance == null){
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
} 
这里为什么要用volatile修饰instance? 
原因:在于instance = new Singleton()的时候,在内存中实际上是分3步执行的:
 
1)分配对象的内存空间:memory = allocate();
 
2)初始化对象:ctorInstance(memory);
 
3)指向分配的地址:instance =memory
 
多线程在执行的时候,2 3可能发生重排序。即有可能线程A执行到第3步的时候,读取到instance不为null,就返回。实际上此时还未执行第二部即未初始化。
 
加上volatile就可以避免2 3步重排序来保证线程安全。
 
 
四、使用静态内部类来实现单例模式
 
package singleton6staticinnerclass;
public class Singleton {
//静态内部类进行初始化
private static class SingletonHandler{
private static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHandler.instance;
}
}
 
五、static代码块来实现单例模式
 
package singleton7static;
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
//静态代码块
static{
instance=new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
 
还有序列化和反序列化的单例模式来实现,或者使用枚举类来实现,在此就不一一介绍了,有兴趣可以百度下。 
