可见性Volatile
About 2 min
可见性
public class VolatileTest {
private static boolean running = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.println("Thread start, time: " + LocalDateTime.now());
while (running) {
}
System.out.println("Thread end, time: " + LocalDateTime.now());
}).start();
TimeUnit.SECONDS.sleep(3);
running = false;
}
}
输出结果:
Thread start, time: 2023-09-04T11:01:56.216820
一直都没能结束,如果在第二行加上volatile
修饰符。那么输出结果如下:
Thread start, time: 2023-09-04T11:03:50.573060
Thread end, time: 2023-09-04T11:03:53.559628
非原子性
public class VolatileTest {
volatile int count = 0;
void increment() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
var volatileTest = new VolatileTest();
var threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(volatileTest::increment));
}
for (Thread thread : threads) thread.start();
for (Thread thread : threads) thread.join();
System.out.println(volatileTest.count);
}
}
输出的结果不确定,但是一般会小于10000
:
38596
解决办法,可以使用提供CAS功能的AtomicInteger
,它既满足原子性要求,还同时拥有可见性。
AtomicInteger count = new AtomicInteger();
void increment() {
for (int i = 0; i < 10000; i++) {
count.incrementAndGet();
}
}
禁止指令重排序
内存读写屏障
指令重排序
FAQ
Lamda表达式里面的异常不能throw?
Long或者Doudble的读写是原子性的吗?
long和double的读写不是原子性的,但是加了volatile就是原子性的。
synchronized 和 volatile 有什么区别?
synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!
- volatile 关键字是线程同步的轻量级实现,所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块。
- volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
- volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。