锁机制(如synchronized关键字)间接解决可见性问题
- 工作原理:
- 当一个线程进入
synchronized
块时,会从主内存中重新读取共享变量的值。
- 当线程退出
synchronized
块时,会将共享变量的最新值刷新回主内存。
- 这就确保了其他线程在进入
synchronized
块时能获取到最新的值,从而间接解决了可见性问题。例如:
public class SynchronizedVisibility {
private static int count = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (SynchronizedVisibility.class) {
count++;
}
});
Thread thread2 = new Thread(() -> {
synchronized (SynchronizedVisibility.class) {
System.out.println("Count value: " + count);
}
});
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
- 在上述代码中,线程
thread1
修改count
变量,因为在synchronized
块中,修改后会将count
刷新回主内存。线程thread2
进入synchronized
块时,会从主内存读取count
,从而保证了可见性。
synchronized与volatile在解决可见性方面的异同点
- 相同点:
- 两者都能解决多线程环境下的可见性问题。即当一个线程修改了共享变量,其他线程能够感知到这种变化。
- 不同点:
- 原子性:
synchronized
关键字既保证可见性,又保证原子性。例如,多个线程同时执行synchronized
修饰的方法或代码块时,同一时间只有一个线程能执行,不会出现数据竞争导致的不一致问题。
volatile
关键字仅保证可见性,不保证原子性。比如,对于volatile int i = 0;
,i++
这样的操作在多线程环境下不是原子的,多个线程同时执行i++
可能会导致结果与预期不符。
- 使用范围:
synchronized
可以修饰方法、代码块,作用范围相对较灵活。可以针对类、对象实例进行加锁。
volatile
只能修饰变量,作用范围相对单一。
- 性能:
synchronized
是一种重量级锁,会导致线程上下文切换等开销,性能相对较低,特别是在竞争激烈的情况下。
volatile
性能开销较小,因为它不会引起线程上下文切换,只是保证变量的可见性。