//单例模式的两种实现方式 懒汉式和饿汉式 /*这两种实现,在单线程的模式下,也不会出现线程安全的问题,但是如果在多线程的情况下,就可能会出现多线程的安全问题 * 改进:双重校验锁 volatile:易变的 动荡的 反复无常的 * 第一次判断singleton是否为null是为了避免singleton已经创建的情况下 避免进入同步代码块 提升效率 * 第二次判断singleton是否为null是为了是为了避免并发情况下A线程经过第一次判断 进入同步代码块 * 此时B线程也经过判断准备进入同步代码块 但是发现同步锁已经被抢占 等待 A线程 释放后B线程进入同步代码块 * 发现此时singleton不为空,所以就不会创建新的对象了。 第二次的判断是很有必要的 * TODO 既然有了Synchronized同步对象锁关键字作为限制 为什么还要加入Volatile呢 * Volatile可以保证可见性和有序性,不能保证原子性 同时保证JVM对指令不会重新排序 * 这一点非常关键,对象的创建不是一步完成的,是一个符合操作,需要3个指令 * singleton = new Singleton() * 指令1:获取singleton对象的内存地址 * 指令2: 初始化singleton对象 * 指令3: 将这块内存地址,指向引用变量singleton * 那么这样我们就可以理解,为什么要加入Volatile关键字了。由于Volatile禁止JVM指令进行重新排序 * 所以创建对象的过程依然是1,2,3 * 但是如果没有Volatile关键字,假设线程A正常创建一个实例,那么执行的顺序可能是 2,1,3 * 当执行到指令1的时候,线程B执行getInstance()方法,获取到的,可能是对象的一部分,或者是不完整的对象 * 程序会报异常信息 * */ class Singleton{//volatile可以保证有序性和可见性 不能保证原子性,所以才和synchronized配合使用 private volatile static Singleton singleton; private Singleton() {//构造方法私有化是为了确保该类的唯一对象实例仅能通过get方法获取 } public static Singleton getSingleton(){ if (singleton == null) {//该if判断是为了减少每次都上锁的开销 synchronized (singleton){ if (singleton == null) {//为了防止A出锁 B进入锁后重复创建对象 singleton = new Singleton(); } } } return singleton; } }
Java单例设计模式
最新推荐文章于 2024-10-06 20:16:20 发布