在Java并发编程中,锁(如synchronized关键字和Lock接口的实现类)和volatile关键字都有特定的内存语义,它们主要影响线程间共享变量的可见性和同步行为。
锁的内存语义
-
互斥性:锁提供了对临界区代码块的互斥访问,同一时刻只能有一个线程持有锁并执行临界区内的代码。
-
可见性:
- 当线程释放锁时,它会强制将该线程对所有受保护的共享变量所做的修改刷新到主内存中。
- 当另一个线程随后获取同一个锁时,它能看到之前线程对共享变量所做的最新更新,从而保证了可见性。
-
有序性:
- 使用锁进行同步可以确保操作的顺序性,即happens-before原则。如果一个线程解锁后另一个线程紧接着获得同一个锁,则前者的写操作必然先行于后者的读或写操作。
volatile内存语义
-
可见性:
- 对volatile变量的写操作会立即刷新到主内存中,并且会导致其他线程中的相应volatile变量值失效,因此当其他线程读取该volatile变量时,总是能够看到最近的写入值。
- 这意味着对volatile变量的修改对于所有线程都是立即可见的。
-
禁止重排序:
- 在Java内存模型下,对volatile变量的操作不会与该变量之前的读写操作发生重排序,这被称为“volatile变量规则”。
然而,volatile并不提供互斥性,也就是说,多个线程同时读写volatile变量时,并不能保证数据一致性。它主要用于以下场景:
- 状态标志的改变通知
- 单例模式中的双重检查锁定(尽管在Java 5以后推荐使用
AtomicReference
等原子类来代替) - 非阻塞数据结构的部分实现
总之,锁用于同步多线程间的互斥访问和整体状态的一致性,而volatile关键字则是在不需互斥但需要保证变量可见性的轻量级同步机制。在实际编程中,根据具体需求选择合适的同步手段至关重要。