Java中的单例模式

前言

  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. 枚举类型不允许被继承
  2. 线程安全且只能被实例化一次
  3. 枚举类型不能被懒加载
 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 }

总结

  1. 控制资源的使用,通过线程同步来控制资源的并发访问
  2. 控制实例的产生,以达到节约资源的目的
  3. 在单例类中定义一些全局变量,多线程可以同时访问此全局变量,达到数据共享的目的。

 

posted @ 2019-06-25 22:32  门外大汉  阅读(853)  评论(0编辑  收藏  举报