参考深入理解Java内存模型 《深入理解Java内存模型》读书总结
1.重排序是怎么一回事?
- 编译器排序 和 运行期重排序
- 访问一个程序变量(对象实例字段,类静态字段和数组元素)可能会使用不同的顺序执行,
而不是程序语义所指定的顺序执行。编译器能够自由的以优化的名义去改变指令顺序。 如果一个线程写 入值到字段a,然后写入值到字段b,而且b的值不依赖于a的值,那么,处理器就能够自由的调整它们的执行顺 序,而且缓冲区能够在a之前刷新b的值到主内存。
不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。
但是对于多线程程序,重排序可能会导致程序执行的结果不是我们需要的结果!因此,就需要我们通过“volatile,synchronize,锁等方式”作出正确的实现同步。保证happen-before
3.JMM (Java memory model)
- Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存 储 在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
线程A与线程B之间如要通信的话,必须要经历下面2个步骤:
- 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
- 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。
3. happen-before
//如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。
程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。 volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。 传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。
4.volatile的作用?
与synchronize一样,都是为了线程安全!但是synchronize是锁住代码块,保证代码执行的原子性,同一时刻,只能有一个对象
获得锁!而volatile则是通过把变量声明为volatile类型,那么就能保证每个线程任一时刻读到的都是最新的值(JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。)
!同时保证了写的原子性,比如,volatile int a ; a = 2; 那么向a中写入2时,JMM会把该线程对应的本地 内存中的共享变量刷新到主内存。
但是 ,对于volatile++ 这些复合操作就不是原子的了!
5. 本地内存 和 主内存?
主内存 —— 即main memory。在java中,实例域、静态域和数组元素是线程之间共享的数据,它们存储在主内存中。
本地内存 —— 即local memory。 局部变量,方法定义参数 和 异常处理器参数是不会在线程之间共享的,它们存储在线程的本地内存中。
我们在某个线程对一个共享数据赋值,倘若不把它定义为volatile类型的话,可能不能立马写入主内存,而是写入本地内存缓冲区,这样,别的线程就不能得到最新的值!除非通过加锁!
相关推荐
Java内存模型通过几种机制来保证可见性,其中volatile关键字是保证共享变量可见性的一种简单方式,它能够禁止指令重排序,并且在每次使用变量前强制从主内存中读取变量的最新值。除此之外,synchronized关键字和Lock...
在JSR133(Java Memory Model规范)之前,`volatile`变量之间不允许重排序,但允许`volatile`和普通变量之间进行重排序。然而,在JSR133之后,对于`volatile`变量与普通变量之间的重排序有了更为严格的限制,确保了`...
`volatile`保证了对于`volatile`变量的读写操作不会被重排序,从而确保了有序性。 然而,`volatile`关键字并不能解决所有并发问题,因为它不具备**原子性**。例如,对于`i++`这样的操作,`volatile`并不能保证其...
2. **有序性**:`volatile`保证了指令重排序不会影响到`volatile`变量的读写。它实现了一个“读屏障”(Load Barrier)和“写屏障”,防止编译器或者处理器优化时对`volatile`变量的读写操作进行重新排序,从而确保...
1. **禁止编译器重排序**:JMM规定,在程序顺序中,当第一个操作是普通变量读写,而第二个操作是`volatile`写时,这两个操作不能被重排序。同样,如果第一个操作是`volatile`读,那么后续的操作也不能被提前到`...
- **有序性**:volatile变量的读写操作具有一定的顺序性,可以阻止指令重排序,确保多线程环境下程序的正确执行顺序。 4. **volatile的局限性** - **非原子性**:volatile不能保证复合操作的原子性,比如`i++`...
为了解决这个问题,我们可以使用volatile关键字来禁止重排序,或者利用类加载机制,通过类初始化阶段的锁来确保线程安全。 总的来说,理解Java并发体系涉及深入学习JMM、线程通信、同步机制和内存模型等多个方面,...
但在旧的JMM中,volatile的语义并不够强大,无法完全避免由于处理器缓存和指令重排序导致的问题。这使得依赖volatile进行线程间通信的代码可能在某些情况下无法正常工作。 final关键字在Java中用来声明不可变对象,...
1. volatile关键字:volatile保证了变量的可见性和禁止指令重排序,但不保证原子性。因此,对于简单的变量赋值操作,volatile可以提供线程安全,但对于复杂操作,如i++这样的操作,仍需额外同步措施。 2. happens-...
- **禁止指令重排序**: 在多线程环境下,JIT编译器可能会对指令进行重排序优化,而`volatile`能够确保操作顺序不会被改变。 **1.3 示例** 例如,在单例模式的实现中,通常采用双重检查锁定(Double-Checked Locking,...
其中,volatile 关键字是 JMM 提供的一种轻量级同步机制,它确保了变量的可见性和禁止指令重排序,使得多线程环境下,对 volatile 变量的修改对其他线程立即可见。 在 JMM 中,happens-before 原则是用来描述操作...
内存模型还引入了重排序的概念,它允许编译器和处理器为了优化性能而重新安排指令的执行顺序,但必须遵守一定的规则以保持正确性。JSR-133对重排序进行了限制,特别是在volatile变量和同步块周围,以确保线程安全。 ...
`volatile`则通过内存屏障和禁止重排序优化,确保了写操作后变量值立即可见,以及读操作时能从主内存中获取最新值。使用`volatile`的条件是写操作不依赖当前值且变量不包含在其他表达式中。 **有序性**涉及到指令重...
它规定了变量的可见性(`volatile`关键字)、内存屏障和指令重排序(`synchronized`关键字的作用),以及线程间的通信方式。开发者应熟练掌握这些概念,以避免并发编程中的陷阱,提高程序的稳定性和性能。
可排序性则涉及到指令重排序,JMM确保线程看到的操作顺序至少与程序的顺序一致,同时考虑内存屏障和volatile变量的限制,以防止乱序执行导致的问题。 **4. 堆和栈** 在Java中,堆内存是所有线程共享的,用于存储...
- 如果不使用`volatile`,可能会因为指令重排序导致`instance`在尚未完成初始化时就被其他线程访问。 ### 2. Java内存模型(JMM) - **概念**:Java内存模型定义了程序中各种变量(线程共享变量)的访问规则,以及...
在并发编程中,指令重排序可能会影响程序的正确性,volatile关键字通过内存屏障来防止指令重排序。volatile与锁都是用来实现多线程间同步,但它们的机制和性能有所不同。 乐观锁和悲观锁是两种并发控制策略。乐观锁...
- **有序性**:确保指令重排序不会破坏单线程内代码的执行顺序,同时保证多线程间必要的同步效果。 3. **volatile关键字** - volatile变量保证了对其他线程的可见性,但不保证原子性。 - 它禁止了指令重排序,...
- **指令重排序**:了解编译器和处理器为了优化性能可能进行的指令重排序,以及它的限制。 6. **并发编程实战** - **死锁**:识别和避免死锁的策略,如避免循环等待、使用死锁检测算法等。 - **线程池优化**:...