本篇文章主要参考于
https://mp.weixin.qq.com/s/dW0L-PoBeTFHhD29HJO0BQ
https://www.cnblogs.com/happy4java/p/11206105.html
特点
- 在java应用中,一个JVM中,该对象只有一个实例存在
- 构造器必须私有(private)
- 没有公开的set方法
- 有公开的get方法,得到唯一的实例
模式介绍
饿汉式
提前new出了对象
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){};
public static HungrySingleton getInstance(){
return instance;
}
}
优点
- 提前new出了对象,省去了创建对象的时间消耗
缺点
- 对于不经常使用的对象,提前创建对象导致浪费了资源
懒汉式
使用时才创建对象
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){};
public static LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
优点
- 使用时才创建,节省了资源
缺点
- 初次调用时,时间过长
- 线程不安全
由于懒汉式,当两个线程同时调用getInstance方法时,都为null则都会创建对象,导致创建多个对象,导致线程不安全。
解决方法:
1.加锁
public class SyncSingleton {
private static SyncSingleton instance;
private SyncSingleton(){};
public static synchronized SyncSingleton getInstance(){
if (instance == null){
instance = new SyncSingleton();
}
return instance;
}
}
缺点:获取实例时加锁 导致系统处理速度下降
优化方向:只有创建对象时加锁
- 双检锁
public class CheckSingleton {
private static CheckSingleton instance;
private CheckSingleton(){};
private static CheckSingleton getInstance(){
//第一次检查,如果不存在实例对象,则上锁
if (instance == null){
//同步块,创建实例
synchronized (CheckSingleton.class){
//如果实例不存在则创建实例
if (instance == null){
instance = new CheckSingleton();
}
}
}
return instance;
}
}
缺点:
- instance = new CheckSingleton();并不是原子操作,jvm会优化执行顺序,导致存在先分配空间,后创建对象的情况,当AB线程同时进入第一个if检查时,A线程先进入同步块,先分配了内存空间后就离开了同步块,还没有创建对象的视乎B进入同步块,然后返回了一块地址,但是这个地址上没有对象导致B引用对象时报错
解决方向:
- 加上volatile修饰的变量
volatile作用:
- volatile修饰的变量不会被线程缓存,当变量改变时,所有线程会更新
- volatile会禁止指令重排序
3.加入volatile
public class CheckSingleton {
private volatile static CheckSingleton instance;
private CheckSingleton(){};
private static CheckSingleton getInstance(){
//第一次检查,如果不存在实例对象,则上锁
if (instance == null){
//同步块,创建实例
synchronized (CheckSingleton.class){
//如果实例不存在则创建实例
if (instance == null){
instance = new CheckSingleton();
}
}
}
return instance;
}
}
缺点:volatile导致重排序失效,损失了一定性能
解决:用静态内部类,类加载时互相排斥的特性创建
4.静态内部类
public class Singleton {
private Singleton(){};
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonFactory.instance;
}
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return getInstance();
}
}
更好的写法-枚举
public enum SingletonEnum {
Instance;
public void doSomeThings(){
System.out.println("hello word");
}
public static void main(String[] args) {
Instance.doSomeThings();
}
}