聊聊volatile

130 阅读2分钟

引言

  • a++:复合操作,不具原子性
import java.util.concurrent.atomic.AtomicInteger;
​
public class DontVolatile implements Runnable {
    volatile int a;
    AtomicInteger realA = new AtomicInteger();
​
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new DontVolatile();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(((DontVolatile) r).a);
        System.out.println(((DontVolatile) r).realA.get());
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            a++;
            realA.incrementAndGet();
        }
    }
}

可见volatile不能保证原子性

那volatile有什么用?

volatile作用

  • 保证可见性

    变量被volatile修饰,每次修改再读取一定能读取到变量更新的值

  • 禁止重排序

as-if-serial:不管怎么重排序,单线程程序执行结果不变

在此前提下,由于CPU优化或编译器,代码实际执行顺序可能和我写顺序不同,单线程ok,但是多线程环境这种乱序会导致线程安全问题,volatile在一定程度禁止重排序

volatile场景

  • 布尔标记位

volatile具有可见性,看代码,此时它的值一旦发生变化,所有线程都可以立刻看到

import java.util.concurrent.atomic.AtomicInteger;
​
public class YesVolatile1 implements Runnable {
    volatile boolean done = false;
    AtomicInteger realA = new AtomicInteger();
​
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new YesVolatile1();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(((YesVolatile1) r).done);
        System.out.println(((YesVolatile1) r).realA.get());
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            setDone();
            realA.incrementAndGet();
        }
    }
​
    private void setDone() {
        done = true;
    }
}
​

由此可见赋值具有原子性

  • 作为触发器,保证其他变量可见性

volatile和synchronized区别

volatile:

  • 可见性

比如一个变量一直只是被各个线程赋值和读取,没有其他操作,可以用volatile替代synchronized或者代替原子变量,足以保证线程安全。实际上,对 volatile 字段的每次读取或写入都类似于 “半同步”——读取volatile 与获取 synchronized 锁有相同的内存语义,而写入 volatile 与释放 synchronized 锁具有相同的语义

synchronized:

  • 可见性
  • 原子性
  • 互斥性

性能方面:voaltile读写操作都是无锁,所以高性能,比synchronized性能高

总结

volatile作用

  • 保证可见性
  • 禁止重排序