双重检查锁单例

阅读: 评论:0

双重检查锁单例

双重检查锁单例

1、简介

双重检查锁单例:使用双重检查锁机制来实现懒汉式单例。它的基本思想是使用两次if 判断,保证在多线程环境下创建单例对象时不会出现竞态条件。

2、优缺点

双重检查锁机制的优点是,在单例对象已经初始化之后,就不需要再进行同步。这样可以提高系统性能。但是,双重检查锁机制的实现过程复杂,需要注意一些细节。例如,使用volatile 关键字修饰 INSTANCE 变量,以及在获取单例对象时使用双重 if 判断。

3、代码实现

下面是使用双重检查锁机制的 Java 代码示例:

public class Singleton {private static volatile Singleton INSTANCE;private Singleton() {}public static Singleton getInstance() {if (INSTANCE == null) {synchronized (Singleton.class) {if (INSTANCE == null) {INSTANCE = new Singleton();}}}return INSTANCE;}
}

4、为什么使用volatile

在代码示例中,使用了volatile 关键字修饰 INSTANCE 变量。这是因为,在 Java 语言中,当多个线程并发执行时,会发生指令重排序。

如果不使用 volatile 关键字,就可能会出现创建单例对象的过程被拆分成多个步骤,例如:

①为单例对象分配内存空间。

②初始化单例对象。

③将INSTANCE 变量指向分配的内存空间。

在没有volatile 关键字的情况下,步骤 ② 和 ③ 可能会被重排序。

这就可能导致其他线程在执行getInstance() 方法时,看到的 INSTANCE 变量已经被赋值,但单例对象并没有被完成初始化。

使用 volatile 关键字,可以保证在多线程环境下,单例对象的初始化过程是串行化的,从而避免了竞态条件。

5、volatile详解

在Java 语言中,volatile 关键字是一种轻量级的同步机制。它主要用于保证每个线程对于变量的读取都是最新的,保证了变量的可见性。

当一个线程对于一个volatile 变量进行修改时,Java 虚拟机会在该线程将修改后的值存回主存之前,强制将该线程中其他变量的缓存数据写回主存。而其他线程在访问该 volatile 变量时,将强制从主存中重新读取该变量的值,这样就能保证变量的可见性。

需要注意的是,volatile 关键字只能保证变量的可见性,不能保证原子性。

如果多个线程同时对同一个 volatile 变量进行修改,可能会出现竞态条件。而且volatile关键字不能替代锁机制来保证线程安全性。如果多个线程同时对一个变量进行修改,而该变量的状态是不确定的,那么就可能会出现问题。

另外, volatile 不能保证复合操作的原子性,如 i++ 不是原子性的操作,volatile 也无法保证原子性。

总结:使用volatile 保证了变量在多线程中可见性,但是不能保证原子性,还需要借助锁机制来实现线程安全。

6、volatile举例说明

当一个变量被volatile 修饰,它会告诉 JVM,这个变量可能会被多线程共享,因此需要进行特殊的处理。

下面是一个使用volatile 关键字的例子:

public class VolatileExample {private volatile boolean flag = false;public void setFlag(){this.flag = true;}public void printValue() {if (flag) {System.out.println("flag is true");}}
}

在这个例子中,flag 是一个共享变量,可能会被多个线程修改。由于flag被volatile修饰,在这里保证多线程修改时读取到最新值。

常用于共享变量的内存可见性,如果没有volatile可能会出现每个线程读取到的变量值不同,或者其他线程不能及时获取到共享变量的最新值,这样会导致错误的结果或系统不稳定。

需要注意的是,volatile并不能保证原子性,如果需要保证变量的操作具有原子性,还需要配合使用synchronized或者Atomic类型。

此外,Java5以后引入了类urrent.atomic.AtomicInteger, AtomicLong, AtomicBoolean, 等类型,它们都有一个显式的变量值和内存屏障(memory barrier),可以在多线程下保证变量更新的可见性和原子性,可以用来代替volatile.

在使用volatile关键字时,编译器与运行时会对它所修饰的变量的访问进行特殊的处理。

编译器会禁止重排序优化,会强制在每次读取该变量的时候,都直接从内存中获取最新的值。

这样可以确保在多线程环境下,所有线程都能够看到共享变量的最新值。

总的来说,使用volatile 关键字的目的是保证共享变量的内存可见性和禁止指令重排序,但不能保证变量操作的原子性,如果需要保证原子性可以使用 Atomic 类型或者加锁。

本文发布于:2024-02-02 22:11:18,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170688307846772.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:锁单例
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23