简介
确保一个类只有一个实例,并提供全局访问点来获取该实例,是最简单的设计模式。
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;
}
}
总结
对象不复杂时,建议使用饿汉式。其他情况下使用懒汉式性能较好。