单例模式详解
定义: 一个类最多只有一个实例,并提供一个全局访问点。就好像我们的 Windows 系统的任务管理器,你只能开一个,不能开多个。
单例模式的种类: 懒汉式 和 饿汉式
实现的必要条件: 私有化构造、静态全局访问
暴力破解: 反射
使用场景:
- 要求生成唯一序列号的环境
- 在整个项目中需要一个共享访问点或共享数据,例如一个 Web 页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
- 创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源;
- 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为 static 的方式)。
1.饿汉式单例
public class SingletonHungry {
private SingletonHungry(){};
private static final SingletonHungry INSTANCE = new SingletonHungry();
public SingletonHungry getSingLeHungry() {
return INSTANCE;
}
}
特点:
(1) 类一创建就生成了对象并加载到内存,会造成内存的浪费。
(2) 线程安全
2.懒汉式单例
2.1.普通方式实现
public class SingletonLazy {
private SingletonLazy() {}
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
特点:
(1) 实现简单
(2) 线程不安全,适合单线程下使用
2.2.实例化方法加锁的实现
public class SingletonLazy_Synchronized {
private SingletonLazy_Synchronized() {}
private static SingletonLazy_Synchronized instance = null;
public static synchronized SingletonLazy_Synchronized getInstance() {
if (instance == null) {
instance = new SingletonLazy_Synchronized();
}
return instance;
}
}
(1) 线程安全
(2) 若频繁调用 getInstance() 方法,不管是否已实例化对象都会唤醒阻塞线程,频繁线程上下文切换导致性能极低。
2.3.双检锁实现
public class SingletonLazy_DoubleCheck {
private SingletonLazy_DoubleCheck() {}
private static volatile SingletonLazy_DoubleCheck INSTANCE= null;
public static SingletonLazy_DoubleCheck getInstance() {
// 实例未被创建,开启同步代码块准备创建
if (INSTANCE == null) {
synchronized (SingletonLazy_DoubleCheck.class) {
// 也许其他线程在判断完后已经创建,再次判断
if (INSTANCE == null) {
INSTANCE = new SingletonLazy_DoubleCheck();
}
}
}
return INSTANCE;
}
}
注意事项: 单例对象必须使用 volatile 修饰保证可见性。否则可能出现指令重排引起的线程安全问题。
new 对象分三步,第二三步可能发生指令重排:
memory = allocate(); // 1.分配对象内存空间
instance(memory); // 2.初始化对象
instance = memory; // 3.设置instance指向刚分配的内存地址,此时 instance != null
特点:
(1) 线程安全
(2) 只有第一次创建对象时才会加锁同步,使之线程安全的同时还能避免每次获取单例对象时的性能消耗
2.4.静态内部类实现
public class SingletonLazy_StaticInnerClass {
private SingletonLazy_StaticInnerClass() {}
// 静态内部类,实例化时自动生成对象
private static class LazyHolder {
private static final SingletonLazy_StaticInnerClass INSTANCE = new SingletonLazy_StaticInnerClass();
}
public static SingletonLazy_StaticInnerClass getInstance() {
return LazyHolder.INSTANCE;
}
}
特点:
(1) 线程安全
(2) 无需加锁,静态内部类第一次被加载时创建对象。保证线程安全的同时还没有加锁时带来的性能消耗,同时也是对象被使用时才创建对象。