java设计模式之单例模式(懒汉,饿汉,双检锁,线程安全问题)
程序员文章站
2022-07-14 09:10:09
...
1.单例模式简单介绍:
1.特点:
·单例类仅有一个实例供其他对象使用
·单例类必须由自己创建这个唯一实例
2.单例创建:
·构造方法私有化 - 防止外部new对象
·提供public static Singleton getInstance()方法 供其他对象获取单例的实例
3.分类:
·饿汉式:类加载时实例化
·懒汉式:调用时实例化
2.代码及解释:
2.1饿汉式:常用
package pers.li.singleton;
/**
* 饿汉式:类加载时初始化,线程安全
* 优点:没有加锁,执行效率会提高。
* 缺点:类加载时就初始化,分配内存。
* 基于 classloader 机制避免了多线程的同步问题,
*/
public class HungrySingleton {
//1.构造方法私有化
private HungrySingleton(){}
//2.私有静态属性
private static HungrySingleton hungrySingleton = new HungrySingleton();
//3.对外公开获取方法
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
2.2懒汉式:线程安全-方法上加synchronized (不推荐)
package pers.li.singleton;
/**
* 线程安全的懒汉式:
* 1.使用synchronized关键字:
* 1.将并行改为串行执行,保证实例化只有一次
* 2.实例化成功后,依旧有锁,那么在并发访问时,等同于读读加锁,性能较差不推荐
*/
public class LazySingletonSafe1 {
//1.构造方法私有化
private LazySingletonSafe1(){}
//2.私有静态属性
private static LazySingletonSafe1 hungrySingleton = null;
//3.对外公开获取方法
public static synchronized LazySingletonSafe1 getInstance(){
//步骤1
if(hungrySingleton == null){
//步骤2
hungrySingleton = new LazySingletonSafe1();
}
//步骤3
return hungrySingleton;
}
}
2.3懒汉式-双检锁 synchronized + volatile
package pers.li.singleton;
/**
* 线程安全的懒汉式:双检锁 + volatile
* 1.多线程时,可能会有多个线程同时执行步骤2,所以可以考虑在步骤2加锁限制,如下
* 2.步骤2加锁后,仍旧有问题:
* 1.当多个线程同时执行到 synchronized时,程序由并行转为串行执行,此时已经解决了创建多个对象的问题
* 2.当线程A执行到步骤2,线程B执行到步骤1的时候,可能会由于JVM指令重排的原因,造成步骤3中返回不完整的实例对象
* 3.对上一步的解释:
* ·指令重排序:是编译器在不改变执行效果的前提下,对指令顺序进行调整,从而提高执行效率的过程
* ·new对象包含的过程:步骤2中 hungrySingleton = new LazySingletonSafe2();
* 1.分配内存
* 2.引用指向内存 hungrySingleton
* 3.内存中存放实例 new LazySingletonSafe2()
* ·由上可知2,3依赖于1,所以指令重排时,2,3可能会交换,交换后会出现问题:
* 当A线程执行到步骤2的时候,指令重排序,导致引用指向了空内存(此时1,3执行,2未执行)
* 同时B线程到达步骤1,发现其引用指向不为null,就会返回一个不完整的实例对象
* ·解决方法:
* 对私有静态属性 添加volatile关键字,保证内存可见性和防止指令重排
* 4.综上:懒汉式线程不安全的两个问题主要来源于:
* 1.创建多个对象 synchronized
* 2.指令重排 volatile
*
*/
public class LazySingletonSafe2 {
//1.构造方法私有化
private LazySingletonSafe2() {
}
//2.私有静态属性
private static volatile LazySingletonSafe2 hungrySingleton = null;
//3.对外公开获取方法
public static LazySingletonSafe2 getInstance() {
//步骤1
if (hungrySingleton == null) {
synchronized (LazySingletonSafe2.class) {
//步骤2
hungrySingleton = new LazySingletonSafe2();
}
}
//步骤3
return hungrySingleton;
}
}