前言
Double-Check虽然是一种巧妙的程序设计方式,但是有可能会抛出空指针的异常,这一切均是由于JVM在运行时指令重排序所导致的,而volatile关键字则可以防止这种重排序的发生。满足多线程程序下的单例、懒加载以及获取实例的高效性。可以这样实现:
private volatile static Singleton instance = null;
volatile以后研究,这里不搞了。
Holder方式
Holder方式完全是借助了类加载的特点。先看看怎样去写,然后简单总结下好处。
1 public class Singleton { 2 3 // 实例变量 4 private byte[] data = new byte[1024]; 5 6 private Singleton() { 7 8 } 9 10 // 静态内部类中持有Singleton的实例,并且可直接被初始化 11 private static class Holder { 12 private static Singleton instance = new Singleton(); 13 } 14 15 // 调用getInstance方法,实际上是获得Holder的instance静态属性 16 public static Singleton getInstance() { 17 return Holder.instance; 18 } 19 }
在Singleton类中并没有instance的静态成员,而是将其放到了静态的内部类Holder之中,所以Singleton类的初始化过程中并不会创建Singleton实例。在Holder类中定义了Singleton的静态变量,并且直接进行了初始化,当Holder被主动引用的时候则会创建Singleton的实例,Singleton实例的创建过程在Java程序编译时期收集至<clinit>()方法中,这个方法又是同步方法,同步方法保证了内存的可见性、JVM指令的顺序性和原子性。Holder方式是目前使用比较广泛的设计之一。
关于类的主动引用和被动引用,类的初始化过程后面会专门来研究,这里不介绍。
枚举方式
首先,关于枚举有几点需要事先声明。
- 枚举类型不允许被继承
- 线程安全且只能被实例化一次
- 枚举类型不能被懒加载
1 public enum SingletonEnum { 2 3 INSTANCE; 4 5 // 实例变量 6 private byte[] data = new byte[1024]; 7 8 SingletonEnum() { 9 System.out.println("new SingletonEnum()"); 10 } 11 12 public static void method() { 13 // 调用这个方法则会主动使用SingletonEnum,INSTANCE将会被实例化 14 } 15 16 public static SingletonEnum getInstance() { 17 return INSTANCE; 18 } 19 }
枚举方式改造+懒加载
1 public class Singleton { 2 3 // 实例变量 4 private byte[] data = new byte[1024]; 5 6 private Singleton() { 7 8 } 9 10 // 使用枚举充当holder 11 private enum EnumHolder { 12 13 INSTANCE; 14 15 private Singleton instance; 16 17 EnumHolder() { 18 this.instance = new Singleton(); 19 } 20 21 private Singleton getSingleton() { 22 return instance; 23 } 24 } 25 26 public static Singleton getInstance() { 27 return EnumHolder.INSTANCE.getSingleton(); 28 } 29 30 }
总结
- 控制资源的使用,通过线程同步来控制资源的并发访问
- 控制实例的产生,以达到节约资源的目的
- 在单例类中定义一些全局变量,多线程可以同时访问此全局变量,达到数据共享的目的。