单例与多线程

一。饿汉模式

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;        
     }        
} 
原文地址:https://www.cnblogs.com/yuyutianxia/p/3833429.html