Java并发中正确使用volatile

简介:

作者:一粟   整理和翻译自Twitter实时搜索的PPT

前几天并发编程群里有同学对volatile的用法提出了疑问,刚好我记得Twitter有关实时搜索的这个PPT对这个问题解释的很清晰并有一个实际的应用场景,于是周末把这个问题摘录了一些和并发相关的内容如下:

并发 – 定义

悲观锁 – Pressimistic locking

  1. 一个线性在执行一个操作时持有对一个资源的独占锁。(互斥)
  2. 一般用在冲突比较可能发生的场景下

乐观锁 – Optimistic locking

  1. 尝试采用原子操作,而不需要持有锁;冲突可被检测,如果发生冲突,具有相应的重试逻辑
  2. 通常用在冲突较少发生的场景下

非阻塞算法 – Non-blocking algorithm

  1. 算法确保对线程间竞争共享资源时候,不会因为互斥而使任一线程的执行无限延迟;

无锁算法 – Lock-free algorithm

  1. 如果系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。
  2. 无锁算法比传统的基于锁的算法对系统的开销更小,且更容易在多核多CPU处理器上扩展;
  3. 在实时系统中可以避免锁带来的延迟;
  4. CAS (compare and swap)或LL/SC(load linked/store conditional),以及内存屏障相关的指令经常被用在算法实现中。

无等待算法 – Wait-free algorithm

  1. 如果每个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好)

Java的并发

  1. Java的内存模型并不保证一个线程可以一直以程序执行的顺序看到另一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(Safe publication)

Java内存模型

代码顺序规则

  1. 一个线程内的每个动作 happens-before 同一个线程内在代码顺序上在其后的所有动作

volatile变量规则

  1. 对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入

传递性

  1. 如果A happens-before B, B happens-before C,那 A happens-before C

Safe publication案例

01 class VolatileExample {
02     int x = 0;
03     volatile int b = 0;
04  
05     private void write() {
06         x = 5;
07         b = 1;
08     }
09  
10     private void read() {
11         int dummy = b;
12         while (x != 5) {
13         }
14     }
15  
16     public static void main(String[] args) throws Exception {
17         final VolatileExample example = new VolatileExample();
18         Thread thread1 = new Thread(new Runnable() {
19             public void run() {
20                 example.write();
21             }
22         });
23         Thread thread2 = new Thread(new Runnable() {
24             public void run() {
25                 example.read();
26             }
27         });
28         thread1.start();
29         thread2.start();
30         thread1.join();
31         thread2.join();
32     }
33 }

x并不需要定义为volatile, 程序里可以有需要类似x的变量,我们只需要一个volatile变量b来确保线程a能看到线程1对x的修改:

  1. 根据代码顺序规则,线程1的x=5; happens-before b=1;; 线程2的int dummy = b; happens-before while(x!=5);
  2. 根据volatile变量规则,线程2的b=1; happens-before int dummy=b;
  3. 根据传递性,x=5; happens-before while(x!=5);

JSR-133

在JSR-133之前的旧Java内存模型中,虽然不允许volatile变量之间重排序,但旧的Java内存模型仍然会允许volatile变量与普通变量之间重排序。JSR-133则增强了volatile的内存语义:严格限制编译器(在编译器)和处理器(在运行期)对volatile变量与普通变量的重排序,确保volatile的写-读和监视器的释放-获取一样,具有相同的内存语义。

延伸阅读: JSR-133: JavaTM Memory Model and Thread Specification, The JSR-133 Cookbook for Compiler Writers

目录
相关文章
|
3月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
74 2
|
3月前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
68 1
|
3月前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
69 0
|
5天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
16 2
|
22天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
25 1
|
11天前
|
SQL 缓存 安全
[Java]volatile关键字
本文介绍了Java中volatile关键字的原理与应用,涵盖JMM规范、并发编程的三大特性(可见性、原子性、有序性),并通过示例详细解析了volatile如何实现可见性和有序性,以及如何结合synchronized、Lock和AtomicInteger确保原子性,最后讨论了volatile在单例模式中的经典应用。
14 0
|
2月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
2月前
|
缓存 Java 编译器
JAVA并发编程volatile核心原理
volatile是轻量级的并发解决方案,volatile修饰的变量,在多线程并发读写场景下,可以保证变量的可见性和有序性,具体是如何实现可见性和有序性。以及volatile缺点是什么?
|
3月前
|
存储 Java
Java 中 ConcurrentHashMap 的并发级别
【8月更文挑战第22天】
50 5
|
3月前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
39 5