Java提供了一种弱同步机制,即volatile变量,用来确保将变量的更新操作通知到其它线程,它是比synchronized关键字更轻的一种同步机制。在当前大多数处理器架构上,读取volatile变量只比读取非volatile变量的开销略高一些。
当一个变量被定义为volatile后,它具备两种特性,第一是保证此变量对所有线程的可见性,指当一个线程修改了这个变量的值,新值对其它线程来说是立即得知的,而普通变量不能做到这点,普通变量的值在线程间传递要通过主内存来完成,如线程A修改一个普通变量的值,然后向主内存进行回写,另外一条线程B在线程A回写完成后再从主内存进行读取操作,新变量的值才会对主线程可见。虽然volatile变量对所有线程都是可见的,但是并不代表它在多线程并发环境下是安全的。
使用volatile变量时注意,volatile变量不足以确保递增操作的原子性,除非能确保只有一个线程对变量执行写操作。也就是声明为volatile的变量当前值与该变量以前的值相关,那么volatile变量将不起作用,即下列表达式都不是原子的:
n=n+1;
n++;
如下代码:
public class MyRunnable implements Runnable {
public static volatile int n = 0;
public void run() {
for (int i = 0; i < 10; i++) {
try {
n = n + 1;
Thread.sleep(10);
}
catch (Exception e) {
// TODO: handle exception
}
}
}
}
public class MainTest {
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[100];
// 创建100个线程
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new MyRunnable());
}
// 启动100个线程
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
// 让100个线程都执行完
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println(MyRunnable.n);
}
}
上面代码,如果对n的操作是原子性的,则输出n=1000,但输出结果不到1000,说明对n的更新不是线程安全的。有人说n++的指令为iinc 1,1,是原子操作,但是把上面n=n+1改成n++后还是出现线程不同步。要想把上面代码改成线程同步,可以在对n的操作中添加synchronized关键字,如下:
synchronized (MyRunnable.class) {
n = n + 1;
}
只有当变量的值和自身的上一个值是无关的volatile关键字才有效,如n=m+1;所以当且仅当满足下面条件时才应该使用volatile变量:
- 对变量的写入操作不依赖变量的当前值,除非能确保只有单个线程更新该变量的值
- 该变量不与其它状态变量一起纳入不变性条件中
- 在访问该变量时不需要加锁
当一个线程被volatile修饰后,它将具备下面两种特性:
1、线程可见性:当一个线程被volatile修改后,无论是否加锁,其它线程都可以看到最新的修改,而普通变量做不到这一点;
2、禁止指令重新排序优化,普通的变量仅仅保证在该方法的执行过程中所有依赋值结果的地方都能获得正确的结果,而不能保证变量赋值操作的顺序与程序代码的执行顺序一致
看下面例子:
import java.util.concurrent.TimeUnit;
public class ThreadStopExample {
private static boolean stop;
public static void main(String[] args) throws InterruptedException {
Thread workThread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stop) {
i++;
System.out.println(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
workThread.start();
TimeUnit.SECONDS.sleep(3);
stop = true;
}
}
关于volatile更深入文章:聊聊并发(一)——深入分析Volatile的实现原理