java中的几种单例模式

目前比较常见的有4种(DCL为懒汉模式的线程安全版本)。

单例模式的实现一般需要满足以下条件:

       1.构造方法私有化,实例属性私有化。

       2.必须仅在类的内部完成实例的初始化过程。

       3.提供公共静态方法,用以返回已经初始化完成的实例。

       4.不可通过反射,反序列化方式获得新的实例。

1.饿汉模式:进行类初始化时就完成实例初始化的方式。可类比月光族,有钱就买。用不用不知道。

                     优势:由于初始化较早,所以相对于其他模式节省了这部分时间,效率较高。且多线程环境中保证安全运行。

                     劣势:在当前单例一直未被使用的场景下,资源利用率变的很低,另外无法从外界传递参数用来实例化。

 1 /**
 2  * @author txba6868
 3  * @title: SingleTonTest
 4  * @projectName javaAnalysis
 5  * @date 2020/1/8 11:08
 6  * @desc 单例模式
 7  */
 8 public class SingleTonTest {
 9 
10     //饿汉式:以空间换时间,类初始化时完成
11     private static SingleTonTest singleTonTest = new SingleTonTest();
12 
13     public static SingleTonTest getInstance() {
14         return singleTonTest;
15     }
16 }

2.懒汉模式:有使用需求时通过调用getInstance()方法实现实例初始化。

                     优势:按需初始化,有效提高资源利用率。第一次调用可动态传参实例化。

                     劣势:初始化时出现时间成本,在多线程环境下有较高可能出现安全问题,造成多次实例化。

 1 /**
 2  * @author txba6868
 3  * @title: SingleTonTest
 4  * @projectName javaAnalysis
 5  * @date 2020/1/8 11:08
 6  * @desc 单例模式
 7  */
 8 public class SingleTonTest {
 9 
10     //懒汉式:以时间换空间,存在多线程安全问题
11     private static SingleTonTest singleTonTest = null;
12 
13     public static SingleTonTest getInstance() {
14         if (singleTonTest == null) {
15             singleTonTest = new SingleTonTest();
16         }
17         return singleTonTest;
18     }
19 }

3.DCL(doule check locking)主要是为了解决懒汉模式带来的线程安全问题做出的优化。但DCL仍旧存在安全问题,主要是因为程序中的“new”操作并非原子操作,代码注释中已写明,涉及到指令重排序及可见性问题,在java1.5之后JMM提供了volatile关键字,通过内存屏障的方式解决了该问题(网上有言论称volatile并未完美解决,仍然存在安全问题,还待博主继续探索下)。并且DCL由于内存屏障的原因,有可能屏蔽一些效率较高的代码优化,反而会引起效率的下降。

 1 /**
 2  * @author txba6868
 3  * @title: SingleTonTest
 4  * @projectName javaAnalysis
 5  * @date 2020/1/8 11:08
 6  * @desc 单例模式
 7  */
 8 public class SingleTonTest {
 9 
10     //构造方法私有化(略)
11     //DCL模式:同样存在线程安全问题,因为"new"操作不是一个原子操作,需要3步执行
12     //1.在内存中申请空间,2.实例中各参数初始化 3.将实例指向内存地址。
13     //由于指令重排序,会导致2和3的顺序发生变化,其他等待实例初始化的线程获取到不完整的实例。
14     private static volatile SingleTonTest singleTonTest = null;
15     
16     public static SingleTonTest getInstance() {
17         if (singleTonTest == null) {
18             synchronized (SingleTonTest.class) {
19                 if (singleTonTest == null) {
20                     singleTonTest = new SingleTonTest();
21                 }
22             }
23         }
24         return singleTonTest;
25     } 
26 }

4.静态内部类:同样是懒加载中的一种初始化方式。类初始化不会引起内部类的初始化。只有调用getInstance()方法时才会执行。该方法是effective java一书中的推荐方式。

                         优势:代码简洁,线程安全(依据为根据java规范,类的初始化有且仅有一次),按需初始化。

                         劣势:同饿汉模式,无法通过传递参数实例化对象。

 1 /**
 2  * @author txba6868
 3  * @title: SingleTonTest
 4  * @projectName javaAnalysis
 5  * @date 2020/1/8 11:08
 6  * @desc 单例模式
 7  */
 8  public class SingleTonTest {
 9  
10  
11     //静态内部类
12      private static class SingleTonInnerClass {
13          private static SingleTonTest singleTonTest = new 
14      SingleTonTest();
15      }
16  
17      public static SingleTonTest getInstance() {
18          return SingleTonInnerClass.singleTonTest;
19      }
20      //构造方法私有化
21      private SingleTonTest(){}
22 
23      
24  }

另外,以上方式都存在两个同样的问题:可通过reflect创建实例;可通过反序列化生成新的实例。当然,我们可以通过在构造函数中加入一个标识阻止实例的二次创建。反序列化时通过重写方法直接返回当前实例。

但这样做无疑增加了代码的复杂性。

5.枚举类型:枚举方式在jvm层面可防御上述两种攻击方式。目前来看是一种较为安全的单例模式,不过枚举方式在内存方面的占用较静态方法略大。

 1 /**
 2  * @author txba6868
 3  * @title: SingleTonTest
 4  * @projectName javaAnalysis
 5  * @date 2020/1/8 11:08
 6  * @desc 单例模式
 7  */
 8 public class SingleTonTest {
 9 
10     //构造方法私有化
11     private SingleTonTest() {
12     }
13 
14     //获取单例对象
15     public static SingleTonTest getInstance() {
16         return SingleTon.INSTANCE.getInstance();
17     }
18 
19     private enum SingleTon {
20         INSTANCE;
21         private SingleTonTest singleTon;
22 
23         SingleTon() {
24             singleTon = new SingleTonTest();
25         }
26 
27         private SingleTonTest getInstance() {
28             return singleTon;
29         }
30     }
31 }

参考:https://www.jianshu.com/p/f8267d6ac017(细说java中的几种单例模式)

引申:https://www.cnblogs.com/duanxz/p/3152574.html(单例-被废弃的DCL双重检查加锁)

           https://www.jianshu.com/p/d9d9dcf23359(为什么说枚举是最好的单例实现)

// 构造方法私有化(略)

猜你喜欢

转载自www.cnblogs.com/txba6868/p/12193397.html
今日推荐