简介
确保一个类只有一个实例,并提供全局访问点来获取该实例,是最简单的设计模式。
UML图:
单例模式共有两种创建方式:
- 饿汉式(线程安全)
提前创建实例,好处在于该实例全局唯一,不存在线程冲突;坏处在于未使用时也会占用内存资源。
- 懒汉式(原生写法存在线程冲突问题)
将实例的创建延迟到第一次使用时进行,相当于懒加载
创建步骤:
- 私有化构造器
- 提供唯一的全局访问接口
一、饿汉式
- 饿汉对象:
public class HungrySingleton {// 创建实例,类加载时已经确定实例,不存在线程冲突private static final HungrySingleton newInstance = new HungrySingleton();// 私有化构造器private HungrySingleton() {}public void processOn() {System.out.println("饿汉单例模式");}/*** 提供对外唯一访问实例的方法** @return*/public static HungrySingleton getInstance() {return newInstance;}
}
- 运行
public class Main {public static void main(String[] args) {HungrySingleton.getInstance().processOn();HungrySingleton hungrySingleton01 = HungrySingleton.getInstance();HungrySingleton hungrySingleton02 = HungrySingleton.getInstance();System.out.println(hungrySingleton02 == hungrySingleton01);}
}
二、懒汉式
- 原生写法
public class LazySingleton {// 实例对象private static LazySingleton instance = null;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
存在线程安全隐患,多个线程进入可能会重复创建实例
改造
- 方式1:同步方法
public class LazySingleton {// 实例对象private static LazySingleton instance = null;private LazySingleton() {}/*** 同步锁,每次只能允许一个线程进行获取* @return*/public static synchronized LazySingleton getInstanceSafely(){if (instance == null) {instance = new LazySingleton();}return instance;}
}
ps:改方式虽然可以确保线程安全,但是由于锁的粒度较大,高并发情况下系统性能会降低。
- 方式2:同步代码块,使用volatile禁止指令重排,确保赋值时的原子操作同时使用DCL双重检查锁定 (Double-Checked-Locking),在多线程情况下保持高性能
public class LazySingleton {// 实例对象,禁止指令重排,确保原子操作private static volatile LazySingleton instance = null;private LazySingleton() {}/*** 同步锁,每次只能允许一个线程进行获取** @return*/public static LazySingleton getInstanceSafely02() {if (null == instance) {synchronized (LazySingleton.class) {// DCL双重检查锁定 if (null == instance) {instance = new LazySingleton();}}}return instance;}
}
总结
对象不复杂时,建议使用饿汉式。其他情况下使用懒汉式性能较好。