synchronized与volatile关键字
synchronized与volatile关键字
1. synchronized关键字
synchronized的工作机制:- 锁机制:当一个线程进入被
synchronized修饰的方法或代码块时,会获取到对象锁。如果另外一个线程进入到同步区域,该线程将会被阻塞。 - 互斥性:
synchronized保证在同一时刻只有一个线程可以执行同步代码块,保持数据一致性。 - 内存可见性:当一个线程修改了共享变量之后,其他线程能看到这个更新的值。
- 锁机制:当一个线程进入被
2. volatile关键字
- Java程序里的每个线程都会把变量拷贝到自己的工作内存里,以便更快访问。可问题来了:多个线程读写同一个变量时,如果各自藏着不同的副本,就好比几个人用自己的小本子记账,迟早对不上。
volatile做的第一件事,就是可见性。- 标记为
volatile的变量,一旦某个线程修改了它,其他线程就能立刻看到最新的值。背后靠的是禁止把它缓存在线程工作内存,并强制把写操作刷新到主内存。 - 第二件事是有序性,也叫“禁止指令重排序”。编译器和CPU会对指令做一些合法的优化,比如把步骤换个顺序以提高效率。
volatile变量的读写会在它前后插入内存屏障(memory barrier),让这部分不能随便乱换顺序,从而让多线程观察到的执行顺序更靠谱。
- 标记为
- volatile 主要用在这样的简单场景:
- 开关控制:比如一个线程控制一个循环是否继续跑。线程A把
keepRunning改成false,线程B就能马上看到并停止循环。1
2
3
4
5
6
7
8
9
10
11
12
13
14volatile boolean keepRunning = true;
// 线程A
void stop() {
keepRunning = false; // 告诉大家别跑了
}
// 线程B
void run() {
while (keepRunning) { // 每次都会检查最新的状态
// ...干活...
}
// 线程A把keepRunning改成false后,这里能很快看到并停下来
}
- 开关控制:比如一个线程控制一个循环是否继续跑。线程A把
volatile不能保证复杂操作的原子性,比如count++这种读-改-写的操作,如果两个线程同时对一个volatile的count做count++,它们可能同时读取到旧值,然后各自加1,最后结果只增加了1,而不是2。
3. 区别
volatile只能用来修饰变量,每次使用这个变量只能去主内存中读取;synchronized可以用来修饰代码块和方法,本质上就是给这段代码块或者方法加锁,要想执行,必须先获得锁。- 由于指令的执行顺序是不可预知的,可能会发生指令重排问题,用
volatile可以避免指令重排;synchronized是一种同步机制,可以用来解决线程安全问题。 volatile能够保证数据的可见性,但不能保证数据的原子性;synchronized两者都能保证。
