大家都知道,一般实现单例有两种写法: 饿汉式 和 懒汉式, 饿汉式是线程安全的,在编译期间已完成初始化,加载到了内存里。 懒汉式一般写法是非线程安全的, 那懒汉式的线程安全单例应该如何实现呢,以及如何写出低耗能的线程安全单例呢 ?
单例实现关键点
- 构造函数私有,private
- 实例对象 静态 且 私有
- 公开获取实例的静态方法
下面我们直接上代码了,按重要程度排序,提供三种方式:
一、高性能的线程安全单例 - 懒汉式
关键注意点:
- volatile 关键字修饰实例对象, 禁止指令重排序
- 加锁实例初始化过程
- 判断实例对象为空时,进行双重校验
package safe;
/**
* @description 线程安全的单例——懒汉式加载
* @author wangyushuai@fang.com
* @date 2018/11/20
*/
public class LazySingleton {
/**
* volatile 修饰属性,简直指令重排序
*/
private static volatile LazySingleton lazySingleton = null;
private LazySingleton() {
//模拟: 创建线程为耗时操作
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static LazySingleton getInstance() {
// 双重校验
if (null == lazySingleton) {
synchronized(LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
public static void main(String args[]) {
for (int i = 0; i < 10; i ++) {
new Thread(() -> {
System.out.println(LazySingleton.getInstance());
}).start();
}
}
}
二、线程安全单例 - 懒汉式
同样是懒汉式,但是这次实现的方式不一样,我们直接选择在 获取实例的方法上,加上同步锁, 但是缺点就是有点消耗性能。
package safe;
/**
* @description 线程安全的单例-懒汉-消耗性能
* 将 Sychronized 关键字加载方法上,消耗性能
* @author wangyushuai@fang.com
* @date 2018/11/20
*/
public class LazySingleton_SyncMethod {
private LazySingleton_SyncMethod() {}
private static LazySingleton_SyncMethod instance = null;
public static synchronized LazySingleton_SyncMethod getInstance() {
if (instance == null) {
instance = new LazySingleton_SyncMethod();
}
return instance;
}
public static void main(String args[]) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(LazySingleton_SyncMethod.getInstance());
}).start();
}
}
}
三、线程安全单例-饿汉式
编译期间,直接完成了初始化。
package safe;
/**
*
* @description 饿汉单例
* @author wangyushuai@fang.com
* @date 2018/11/20
*/
public class HungerSingleton {
private static HungerSingleton ourInstance = new HungerSingleton();
public static HungerSingleton getInstance() {
return ourInstance;
}
private HungerSingleton() {
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(HungerSingleton.getInstance());
}).start();
}
}
}