Skip to main content

可见性Volatile

huhxAbout 2 minjavaThreadConcurrency

可见性

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 关键字解决的是多个线程之间访问资源的同步性。

总结

参考