文章目录
  1. 1. 饿汉
  2. 2. 懒汉
  3. 3. 双重校验锁
  4. 4. 静态内部类
  5. 5. 枚举
  6. 6. 反射机制的攻击
  7. 7. 单例模式的序列化

作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。单例模式有多种实现方式,下面就详细介绍一下。

饿汉

public class Singleton {  
      private static Singleton singleton = new Singleton();
    private Singleton() {}  

    public static Singleton getInstance() {  
        return singleton;  
    }  
}

类加载的时候就创建实例,不能lazy loading;可以被反射机制攻击;但是线程安全。

懒汉

public class Singleton {  
      private static Singleton singleton = null;
    private Singleton() {}  

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

Lazy loading;线程安全;效率比较低,因为需要线程同步的时候比较少;需要额外的工作(Serializable、transient、readResolve())来实现序列化。

双重校验锁

public class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(instance == null){
            //同步块,线程安全的创建实例
            synchronized (Singleton.class) {
                //再次检查实例是否存在,如果不存在才真正的创建实例
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

“双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。但是只能用在java5及以上的版本。

另外,由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。

静态内部类

public class Singleton {  
    private Singleton() {}  

    private static final class  Holder {  
        static final Singleton instance = new Singleton();  
    }  

    public static Singleton getInstance() {  
        return Holder.instance;  
    }  
}

在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:

  1. 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
  2. 访问final字段时
  3. 在创建线程之前创建对象时
  4. 线程可以看见它将要处理的对象时

当getInstance方法第一次被调用的时候,它第一次读取Holder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

  这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。   

枚举

public enum Singleton{
    INSTANCE;
    public void doSomeThing(){
    }
}

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

反射机制的攻击

通过Constructor<?>[] getDeclaredConstructors() 这个方法获得所有构造器,然后可以得到私有的构造器;使用 cons. setAccessible(true) 突破私有构造。然后可以创建无限个单例实例

单例模式的序列化

如果你序列化一个单例类,然后两次重构它,那么你就会得到那个单例类的两个实例,除非你实现readResolve()方法。如下:

public class Singleton implements java.io.Serializable {   
   public static Singleton INSTANCE = new Singleton();   

   protected Singleton() {   
      // Exists only to thwart instantiation.    
   } 

   private Object readResolve() {   
            return INSTANCE;    
   }   
}   
文章目录
  1. 1. 饿汉
  2. 2. 懒汉
  3. 3. 双重校验锁
  4. 4. 静态内部类
  5. 5. 枚举
  6. 6. 反射机制的攻击
  7. 7. 单例模式的序列化