引言
- 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作用
- 保证可见性
- 禁止重排序