Java并发
Java内存模型
Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理 硬件时提到的主内存名字一样,两者也可以类比,但物理上它仅是虚拟机内存的一部分)。每条线程 还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对变量的所有操作(读取、赋值等)都必须在工作内 存中进行,而不能直接读写主内存中的数据。不同的线程之间也无法直接访问对方工作内存中的变 量,线程间变量值的传递均需要通过主内存来完成,
![[Pasted image 20230520212252.png]]
volatile关键字
特性
- 保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
- 禁止指令重排序优化
并发下的问题
以下的代码,因为Java中运算符并非原子操作导致volatile变量在并发下发出现问题。
1 | /** |
运行程序会发现运行结果是小于200000的,问题出在race++并不是一个原子操作。
使用的场景
可见性
- 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
- 变量不需要与其他的状态变量共同参与不变约束。
如以下代码:
1 | volatile boolean shutdownRequested; |
禁止指令重排
以下是我们常用的双锁检测单例代码:
1 | public class Singleton { |
在多线程环境中,如果没有使用volatile关键字修饰instance变量,就可能出现双重检查锁定失效的情况。具体来说,由于指令重排的存在,一个线程可能会在第一次检查instance是否为null时得到false,但在执行完同步块之后,instance还没有被赋值,这时候如果有另一个线程访问getInstance()方法,就会再次创建新的实例,从而破坏单例模式。
使用volatile关键字可以禁止指令重排,保证instance变量的可见性和有序性,从而使双重检查锁定生效。当一个线程修改了volatile变量的值,其他线程能够立即看到修改后的最新值,从而避免了上述问题。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 BravestSnail's Blog!