作用
如果对共享变量除了赋值之外并不完成其他操作,那么可以将这些共享变量声明为volatile。
原理
volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域可能被另一个线程并发更新。
private volatile boolean done;
public boolean isDone(){ return done};
public void setDone(){done = true};
当然也可以使用锁来实现:
private boolean done;
public synchronized boolean isDone(){return done;}
public synchronized void setDone(){done = true;}
相对来说,使用锁显得开销过大了,volatile就更合适。但是volatile变量不能提供原子性,例如方法
public void flipDone(){done = !done;}
不能确保翻转域中的值,不能保证读取、翻转和写入不被中断。
参考:《Java核心技术 卷1》 第14章:并发
不保证原子性
举个累加的例子
public class VolatileTest01 implements Runnable{
private volatile static int count = 0; //并不保证原子性
public static void main(String[] args) {
Runnable vt = new VolatileTest01();
Thread t1 = new Thread(vt, "A");
Thread t2 = new Thread(vt, "B");
t1.start();
t2.start();
}
@Override
public void run() {
for(int i = 0; i < 10000; i++){
count++;
}
System.out.println(Thread.currentThread().getName() + "-->count = " + count);
}
}
该段代码不能保证原子性,输出的结果不确定,我一次输出的结果如图:
如果使用锁的方法就可以保证原子性了
public class VolatileTest02 implements Runnable{
private static int count = 0;
public static void main(String[] args) {
Runnable vt = new VolatileTest02();
Thread t1 = new Thread(vt, "A");
Thread t2 = new Thread(vt, "B");
t1.start();
t2.start();
}
@Override
public synchronized void run() { //使用锁保证了原子性
for(int i = 0; i < 10000; i++){
count++;
}
System.out.println(Thread.currentThread().getName() + "-->count = " + count);
}
}
每次输出的结果都为确定的: