关于Java中的volatile型变量

来源:互联网 发布:企业门户免费源码 编辑:程序博客网 时间:2024/05/29 03:16

Java语言中使用volatile关键字实现了一种弱同步机制。大多数场景下使用volatile变量的同步开销要比使用锁来的低,当一个变量被声明为volatile后,它将具备两种特性:


1、保证被声明volatile的变量对所有线程的可见性,这种“可见性”是指当一个线程修改了这个变量,新值对其它线程是可以立即得知的。但要注意,如果对volatile变量进行非原子操作也会引发安全性问题。

public class VolatileTest1 {private static final int THREADS_COUNTS = 20;public static volatile int race = 0;public static void increase() {race++;}//mainpublic static void main(String args[]) {Thread[] threads = new Thread[THREADS_COUNTS];//创建20个线程,每个线程对increase()方法调用1000次for (int i = 0; i < THREADS_COUNTS; i++) {threads[i] = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {increase();}}});threads[i].start();}while(Thread.activeCount() > 1) {Thread.yield();}System.out.println(race);}}
上面这个例子期望的结果是200000,但运行程序并不能获得期望结果。原因就出现在“race++”上。由于这个自增运算不是原子操作,所以多个线程运行increase()方法时可能会对已经过期的数据进行操作,导致最终出现意外的结果。

对于volatile变量的一种典型用法是:检查某个状态标记以判断是否退出循环

volatile boolean shutdown;public void shutdown() {shutdown = true;} public void doWork() {while(!shutdown) {//do somethings ...}}

2、禁止指令重排序优化。

/** * 此例子在client模式下从运算结果来看说明不了重排序的问题, * 这里只是用程序逻辑还有主线程和子线程的关系 * 来说明重排序可能带来的影响。 * */public class VolatileTest2 extends Thread{//private boolean volatile initialized;private boolean initialized;/** * 初始化任务 * @throws InterruptedException */public void init() throws InterruptedException {System.out.println("start init ...");Thread.sleep(10000); //模拟一个长时间的初始化操作System.out.println("end init ...");//下面这行代码可能被提前执行,导致初始化工作还未完成,//可能就开始了运算任务。如果initialized被声明为volatile//则不会有此问题initialized = true; }public void run() {while(!initialized) {try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("start my works ...");//do somethings ...}public static void main(String args[]) throws InterruptedException {VolatileTest2 vt = new VolatileTest2();vt.init(); //主线程执行初始化操作vt.start(); //启动一个子线程,执行运算}}

参考:《深入理解Java虚拟机:JVM高级特性与最佳实践

    《Java并发编程实践》

原创粉丝点击