volatile原理
参考这篇大佬的博客点我
volatile关键字的作用
1.保证变量的可见性
多线程下,被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。网上有这样一段多线程脏读共享变量的代码验证,本文进行了参考点我
public class VolatileTest3 {
static class Work {
boolean isShutDown = false;
void shutdown() {
isShutDown = true;
System.out.println("shutdown!");
}
void doWork() {
while (!isShutDown) {
System.out.println("doWork");
}
}
}
public static void main(String[] args) {
Work work = new Work();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::shutdown).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
}
}
运行结果如下:
doWork
doWork
doWork
doWork
doWork
doWork
doWork
doWork
doWork
doWork
doWork
shutdown!
doWork
但是,经过分析发现,此代码不能证明脏读。上面的代码中有1、2、3、4、5、6、7七个线程,其中第四个线程是执行shutDown方法,其余线程全部执行work方法。如果第四个线程在执行shutDown方法时,有某个线程已经进入了work方法的while循环,此时不管isShutDown的值有没有变化,都会输出打印语句,因此上面代码不能很好的验证脏读。稍微改造一下代码:
while (!isShutDown) {
try {
Thread.sleep(20L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doWork"+",isShutDown="+isShutDown);
}
输出结果如下:
shutdown!
doWork,isShutDown=true
doWork,isShutDown=true
doWork,isShutDown=true
2.防止字段的重排序
3.volatile关键字修饰的变量线程不安全
volatile关键字修饰的变量依赖本身的值变化就不能保证线程安全。因此volatile变量不能用作计数器之类的功能。验证如下:
public class VolatileTest2 {
private static volatile int ticket = 10;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 3; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
while (true) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
} else {
break;
}
}
}
});
}
executorService.shutdown();
}
}
输出如下:
pool-1-thread-1:10
pool-1-thread-2:9
pool-1-thread-3:8
pool-1-thread-1:7
pool-1-thread-3:6
pool-1-thread-2:5
pool-1-thread-1:4
pool-1-thread-2:2
pool-1-thread-3:3
pool-1-thread-1:1
pool-1-thread-3:-1
pool-1-thread-2:0
从结果中可以看到,结果中出现了-1,如果ticket值大一点,还会出现大量的重复值。结论就是变量即使用volatile修饰了但依旧出现了线程安全问题。