文章目录
- 设计模式-单例模式
- 懒汉模式
- 怎么解决线程安全问题?
- 1.使用双重校验锁
- 修饰实例对象,禁止指令重排序
- 线程安全的懒汉模式
- 饿汉模式
设计模式-单例模式
啥是单例模式?
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1.在类中就创建实例,只创建一个实例
2.类外如果想要调用这个对象必须调用getInstance方法获取实例,好处是减少了创建对象、销毁对象的开销
3.获取实例只能获取到类提供的唯一实例
懒汉模式
解释一下懒汉,懒汉就是懒加载,在类加载的时候不会去创建实例,在第一次调用的时候才会创建实例。
public class Singleton {
/**
* 以下是懒汉模式的代码
*/
// 会产生线程安全问题,使用双重校验锁来保证安全
private static Singleton instance = null;
// 类加载过程不创建实例,实例设置为null
private Singleton() {
}
public static Singleton getInstance(){
// 第一次调用该类才会创建实例
if(instance==null){
return instance = new Singleton();
}
// 其后每次调用实例都使用上面创建的实例
return instance;
}
}
好处: 只有调用资源的时候才会创建实例,所以节约了资源。
坏处:懒汉模式会带来线程安全问题
如果多个线程同时获取实例,都是第一次获取,对执行 if(instance==null) 这个语句中的new实例,所以会创建出多个实例来。
怎么解决线程安全问题?
1.使用双重校验锁
第一个if(instance==null)
先判断实例是否存在,不存在再加锁
第二个if(instance==null)
当多线程都是第一次调用该对象的时候,只有一个线程能够进入,避免创建多个对象
public static Singleton getInstance(){
if (instance==null) { // 先判断实例是否存在,不存在再加锁
synchronized (Singleton.class) {
if(instance==null){// 多线程情况下,只有一个能进入,避免创建多个对象。
return instance = new Singleton();
}
}
}
return instance;
}
修饰实例对象,禁止指令重排序
private volatile static Singleton instance = null;
Instance = new Singleton 这段代码其实分成三步骤
1.为 new Singleton 分配内存空间
2.初始化对象实例
变量指向这块内存地址
初始化时间有点慢,所以JVM指令重排,顺序变了1->3->2,在单线程的情况下没啥问题,但是如果在多线程的情况下,第一个线程进去了,执行了1-3,这个时候第二个线程一个 实例不为null,直接返回了没有初始化 的实例对象,明显产生了线程安全问题。
所以使用 volatile 禁止指令重排。
线程安全的懒汉模式
public class Singleton {
/**
* 以下是懒汉模式的代码
*/
// 会产生线程安全问题,使用双重校验锁来保证安全,使用volatile禁止指令重排序
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance(){
if (instance==null) {
synchronized (Singleton.class) {
if(instance==null){
return instance = new Singleton();
}
}
}
return instance;
}
}
饿汉模式
理解饿汉,因为太饿了,所以一拿到东西就想吃,这里就是类加载的过程就直接创建唯一实例。
好处:没有线程安全问题
坏处:不像懒汉模式调用才创建实例,这个是类加载就创建实例,所以如果一直没调用,也得创建实例浪费资源。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
// 调用方法获取实例,只有读,所以没有线程安全问题
public static Singleton getInstance(){
return instance;
}
}