在并发编程中,volatile是一个很重要的存在。它可以理解为是一个轻量级的synchronized。在多线程开发中,线程之间修改变量默认是不可见的。
volatile特性
- 能够保证多个线程对共享变量的可见性。即一个线程修改了某个共享变量的值,修改后的值对其他线程可见。
- 禁止指令重排(有序性)。
- volatile 只能保证对单次读/写的原子性。类似于i++ 这种操作不具有原子性。
基础使用
演示如下:
public class VolatileBase {
private static boolean flag;
private static class MyThread implements Runnable{
@Override
public void run() {
while (!flag){
}
System.out.println("running");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread());
thread.start();
//让子线程先执行
TimeUnit.SECONDS.sleep(1);
flag=true;
TimeUnit.SECONDS.sleep(5);
System.out.println("main is ended!");
}
}
根据演示结果,可以看到,子线程会在while中无限循环,就算主线程将flag修改为true,子线程也是无法知道的。验证了线程间变量的不可见性。
此时如果要在多线程下,保证共享变量的可见性,就需要在共享变量前添加volatile关键字。那何为可见性呢? 也就是说当一个线程修改一个共享变量时,另外一个线程能读到被修改的值。演示效果如下:
public class VolatileBase {
private static volatile boolean flag;
private static class MyThread implements Runnable{
@Override
public void run() {
while (!flag){
}
System.out.println("running");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread());
thread.start();
TimeUnit.SECONDS.sleep(1);
flag=true;
TimeUnit.SECONDS.sleep(5);
System.out.println("main is ended!");
}
}
根据演示结果,当共享变量添加了volatile后,子线程可以感知到变量值的改变,实现了多线程下共享变量的可见性。
使用volatile是否能保证数据一致性
volatile的一个特性就是,当共享变量存在计算时,其是无法保证原子性的,具体演示如下:
public class SafeDemo {
private volatile long count = 0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public void incrCount(){
count++;
}
public static class MyThread implements Runnable{
private SafeDemo safeDemo;
public MyThread(SafeDemo safeDemo) {
this.safeDemo = safeDemo;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
safeDemo.incrCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
SafeDemo safeDemo = new SafeDemo();
Thread thread1 = new Thread(new MyThread(safeDemo));
Thread thread2 = new Thread(new MyThread(safeDemo));
thread1.start();
thread2.start();
TimeUnit.SECONDS.sleep(1);
System.out.println(safeDemo.count);
}
}
根据演示可以看到,其并不能保证共享数据的一致性,volatile只保证可见性。
其他更深层次的内容,如指令重排、内存语义、写底层、读底层等,都需要理解JMM(Java内存模型),后续会有专门章节深入讲解。
网友评论