设计模式系列—单例设计模式( 二 )

/** * 懒汉式(线程安全 , 同步方法) */public class Singleton {private static Singleton instance;// 构造函数私有化private Singleton() {}// 提供一个静态的公有方法 , 加入同步处理的代码 , 解决线程安全问题public static synchronizedSingleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}注:加入关键字保证线程安全问题 synchronized
优缺点

  • 虽然加入保证线程安全的关键字 , 解决线程安全问题(其实实际上并不一定线程安全:编译器(JIT),CPU 有可能对指令进行重排序)
  • 效率低下 , 每个线程在想获得实例的时候 , 执行 getInstance 方法都要进行同步 。 方法的同步效率太低 。
在实际开发中 , 不推荐这种方式
懒汉式(线程安全 , 同步代码块)代码示例/** * 懒汉式(线程安全 , 同步代码块) */public class Singleton {private static Singleton instance;// 构造函数私有化private Singleton() {}// 提供一个静态的公有方法 , 加入同步处理的代码 , 解决线程安全问题public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {instance = new Singleton();}}return instance;}}优缺点虽然对加锁处做了一定优化 , 总体优缺点同上面一致 。
双重检查(DCL)代码示例/** * 双重检查 DCL */public class Singleton {// 添加 volatile 关键字 , 防止指令重排private static volatile Singleton instance;// 构造函数私有化private Singleton() {}// 提供对外引用的静态方法 , 使用双重检查机制 double checkpublic static Singleton getInstance() {if (instance == null) { // Double Check Lock// 双重检查synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}}编译器(JIT),CPU 有可能对指令进行重排序 , 导致使用到尚未初始化的实例 , 可以通过添加volatile 关键字进行修饰
优缺点
  • Double-Check 概念是多线程开发中常使用到的 , 如代码中所示 , 我们进行了两次 if(singleton== null) 检查 , 这样就可以保证线程安全了
  • 实例化代码只用执行一次 , 后面在进行访问的时候 , 判断 if(singleton== null) , 直接 return 实例化对象 , 也避免了反复进行方法同步 。
最重要的是 线程安全 , 延迟加载 , 效率高 在实际开发中 , 推荐使用这种单例设计模式
静态内部类代码示例package com.niuh.designpattern.singleton.v7;/** * 静态内部类完成 , 推荐使用 */public class Singleton {// 添加 volatile 关键字 , 防止指令重排private static volatile Singleton instance;// 构造函数私有化private Singleton() {}// 写一个静态内部类 , 该类中有一个静态属性 Singletonprivate static class SingletonInstance {privatestatic final Singleton INSTANCE = new Singleton();}// 提供一个静态的公有方法 , 直接返回 SingletonInstance.INSTANCEpublic static synchronized Singleton getInstance() {return SingletonInstance.INSTANCE;}}优缺点
  • 本质上是利用类的加载机制来保证线程安全
  • 只有在实际使用的时候 , 才会触发类的初始化 , 所以也是懒加载的一种形式 。
  • 优点:避免了线程不安全 , 利用静态内部类特点实现延迟加载 , 效率高
静态内部类的方式在 singleton 类被装载时并不会被立即实例化 , 而是在需要的实例化的时候 , 调用 getInstance 方法 , 才会装载 SingletonInstance 类 , 从而完成 Singleton 的实例化 类的静态属性只会在第一次加载类的时候初始化 , 所以在这里 , JVM 帮助我们保证了线程的安全性 , 在类进行初始化的时 , 别的线程是无法进入的 。推荐上实际开发中使用
枚举代码示例package com.niuh.designpattern.singleton.v8;/** * 使用枚举 , 可以实现单例, 推荐 */public enum Singleton {INSTANCE; // 属性public void hello() {System.out.println("hello word~");}} class SingletonTest08 {public static void main(String[] args) {Singleton instance1 = Singleton.INSTANCE;Singleton instance2 = Singleton.INSTANCE;System.out.println(instance1 == instance2);System.out.println(instance1.hashCode());System.out.println(instance2.hashCode());instance1.hello();}}优缺点
  • 天然不支持反射创建对应的实例 , 且有自己的反序列化机制
  • 利用类加载机制保证线程安全
这借助 JDK1.5 中添加的枚举来实现单例模式 。 不仅能避免多线程同步问题 , 而且还能防止反序列化重新创建新的对象 , 推荐使用 。
反射攻击实例我们拿饿汉模式作为例子进行测试 , 饿汉模式的代码如下:
package com.niuh.designpattern.singleton.reflect;/** * 饿汉模式防止反射攻击 */public class HungrySingleton {private static final HungrySingleton instance;private HungrySingleton(){}static {instance = new HungrySingleton();}public static HungrySingleton getInstance(){return instance;}private Object readResolve(){return instance;}}