逐步构建一个“铜墙铁壁”的单例模式

本文详细介绍了如何逐步构建一个线程安全、防反射和序列化破坏的单例模式,从普通实现、线程安全的饱汉式、静态内部类方式,到枚举实现,再到Unsafe类的使用。通过逐步迭代优化,揭示了单例模式的实现原理和潜在的破坏手段,最终得出枚举是实现单例模式的安全选择。
摘要由CSDN通过智能技术生成

单例模式被公认为是设计模式中最简单的一种,用于保证系统中,某个类只有一个实例,运用非常广泛。

单例模式,往简单了说,其实关键就是,控制构造函数的访问权限,然后对外提供统一的访问点。

但其实,写好一个单例模式,并没有大家想的那么简单。下面,通过一步步的迭代优化,从线程安全和防破坏两个维度,逐步的实现一个“铜墙铁壁”般牢固的单例模式demo。

主要包含如下几块内容:

  1. 普通饱汉式和饿汉式

  2. 线程安全的饱汉式 (加锁和DCL)

  3. 静态内部类方式

  4. 单例模式的破坏

  5. 终极大法:枚举

  6. 一把无坚不摧的矛:Unsafa类

  7. 总结

一:普通实现方式

普通的饱汉式和饿汉式单例模式实现,应该是大家接触的最多的实现方式,他们实现简单,便于理解。

饱汉式的优势在于懒加载,对于非常消耗资源,占内存的对象尤其有效,但其实线程不安全的。

而饿汉式的优势在于,其天生的线程安全性。因此在大多数的场景下,饿汉式的单例模式已经够用。

 

二:线程安全的饱汉式

第一节讲了,饱汉式的实现方式是线程不安全的,因为它的非空判断和初始化是多步操作,不是原子的。

最简单的方式,通过synchronize关键字直接给方法加锁,但这样的方式比较低效,比较简单,这里就不过多阐述了。

更好的方式是DCL (Double Check Lock),双重check机制。第一个check是为了对象已经创建后,产生不必要的同步。第二个check,是避免第一个判空之后,进入同步方法前,有其他线程创建了实例。

需要注意的是,instance这个属性的volatile关键字,因为实例的创建不是原子操作,它包含了:(1) 分配内存,(2) 初始化对象,(3) 引用指向新的内存空间 三个步骤,其中2依赖于1,但是3不依赖于2,所以由于CPU指令重排序的影响,其他线程可能看到的是“半个”对象,而加上volatile关键字就是为了避免指令重排序。

public class TestSingleton {

   public static void main(String[] args) {

       // 多线程环境下,创建实例
       final Map<String, HungrySingleton> HungryMap = new ConcurrentHashMap(10);
       final Map<String, FullSingleton> FullMap = new ConcurrentHashMap(10);
       final Map<String, DCL> DCLMap = new ConcurrentHashMap(10);

       for (int i = 0; i < 1000; i++) {
           final int threadIndex = i;
           new Thread(new Runnable() {
               public void run() {
                   HungryMap.put("thread" + threadIndex, HungrySingleton.getInstance());
                   FullMap.put("thread" + threadIndex, FullSingleton.getInstance());
                   DCLMap.put("thread" + threadIndex, DCL.getInstance());
               }
           }).start();
       }

       // 通过set的size大小,来判断是否创建了不同的实例
       Set<HungrySingleton> hungrySingletonSet = new HashSet<HungrySingleton>();
       hungrySingletonSet.addAll(HungryMap.values());
       System.out.println("饿汉式单例多线程下是否产生了不同的对象:" + (hungrySingletonSet.size() > 1));// 偶尔会为:true

       Set<FullSingleton> FullSingletonSet = new HashSet<FullSingleton>();
       FullSingletonSet.addAll(FullMap.values());
       System.out.p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值