`
unnKoel
  • 浏览: 13758 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

java volatile

    博客分类:
  • java
阅读更多

1.volatile并不能保证线程安全性

   声明为volatile的线程共享变量,并不能保证多线程在修改它值的时候,是安全的。

   测试代码如下:

  

package com.wind.DbTec.sqlpkg;


/**
 * volatile变量的正确用法 volatile变量不能保证变量修改的原子性,需要原子类的协助 基于解析可看链接地址
 * http://www.cnblogs.com/yakun/p/3589437.html
 * http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
 * http://www.blogjava.net/mstar/archive/2013/04/24/398351.html
 * 
 * @author zhouyanjun
 * @version 1.0 2014-12-10
 */
public class VolatileTest extends Thread {

	static class VolatileRunnable implements Runnable {

		private volatile Integer value = 0;

		public void run() {
			for (;;) {
				if (value >= 1000) {
					break;
				}
				value += 1;
				System.out.println(Thread.currentThread().getName() + " " + value);
			}
		}
	}

	public static void main(String[] args) {
		VolatileRunnable volatileRunnable = new VolatileRunnable();
		Thread thread1 = new Thread(volatileRunnable);
		Thread thread2 = new Thread(volatileRunnable);
		Thread thread3 = new Thread(volatileRunnable);
		Thread thread4 = new Thread(volatileRunnable);
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
	}
}

    如上述代码,声明了一个线程共享的volatile的变量value,并在线程处理中累加它的值,同时启动4个线程,我们来看打印出的value值是否有相同值,如果有相同则表明volatile变量并不能保证线程安全。

    笔者在多次执行下来,确实存在有相同值的情况(结果集太长就不贴出来了),所以volatile变量并不能保证线程安全。读者也可以自己测试下。

 

2.如何能使volatile变量线程安全呢?

    要使volatile变量线程安全,是可以做到的,需要配合一些原子类的实现。比如笔者下面使用AtomicReferen-ceFieldUpdater类修改的上述代码,就可以保证volatile变量的线程安全。代码如下:

   

package com.wind.DbTec.sqlpkg;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * volatile变量的正确用法 volatile变量不能保证变量修改的原子性,需要原子类的协助 基于解析可看链接地址
 * http://www.cnblogs.com/yakun/p/3589437.html
 * http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
 * http://www.blogjava.net/mstar/archive/2013/04/24/398351.html
 * 
 * @author zhouyanjun
 * @version 1.0 2014-12-10
 */
public class VolatileTest extends Thread {

	static class VolatileRunnable implements Runnable {

		private static final AtomicReferenceFieldUpdater<VolatileRunnable, Integer> nextUpdater = AtomicReferenceFieldUpdater
			.newUpdater(VolatileRunnable.class, Integer.class, "value"); // 增加一个AtomicReferenceFieldUpdater

		private volatile Integer value = 0;

		public void run() {
			for (;;) {
				if (value >= 1000) {
					break;
				}
				// 递增value当前值
				Integer current = value;
				while (!nextUpdater.compareAndSet(this, current, current + 1)) { // 判断value值是否跟原值一样,一样则修改value值为current+1
					current = value;
				}

				System.out.println(Thread.currentThread().getName() + " " + value);
			}
		}
	}

	public static void main(String[] args) {
		VolatileRunnable volatileRunnable = new VolatileRunnable();
		Thread thread1 = new Thread(volatileRunnable);
		Thread thread2 = new Thread(volatileRunnable);
		Thread thread3 = new Thread(volatileRunnable);
		Thread thread4 = new Thread(volatileRunnable);
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
	}
}

   这种情况下,笔者多次测试下来,确实没出现相同值的情况,保证了volatile值的线程安全性。具体AtomicR-eferenceFieldUpdater类的实现可以从java.util.concurrent.atomic包下找到。

 

   其实java.util.concurrent.atomic包下针对int,long,boolean,reference实现了好几个原子类,比如AtomicInteg-er,AtomicLong等。他们的原子更新方法实现,跟上述代码其中的这段很相似

Integer current = value;
while (!nextUpdater.compareAndSet(this, current, current + 1)) { // 判断value值是否跟原值一样,一样则修改value值为current+1
	current = value;
}

  比如AtomicInteger,它的值保存在成员变量value中,而且value是个volatile变量,如下:

  

 private volatile int value;

 

  它的getAndSet方法(此方法是返回当前值,然后设置新值)实现如下:

  

    /**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

 

3.为什么volatile还需要配置原子类才能保证线程安全?

   这个原因可能需要细究到java虚拟机的实现中去,笔者暂且止步,再次记一笔,以后有机会再做细究。

 

 

0
0
分享到:
评论
1 楼 在世界的中心呼喚愛 2014-12-17  
package com.wind.DbTec.sqlpkg;  
  
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;  
  
/** 
 * volatile变量的正确用法 volatile变量不能保证变量修改的原子性,需要原子类的协助 基于解析可看链接地址 
 * http://www.cnblogs.com/yakun/p/3589437.html 
 * http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html 
 * http://www.blogjava.net/mstar/archive/2013/04/24/398351.html 
 *  
 * @author zhouyanjun 
 * @version 1.0 2014-12-10 
 */  
public class VolatileTest extends Thread {  
  
    static class VolatileRunnable implements Runnable {  
  
        private static final AtomicReferenceFieldUpdater<VolatileRunnable, Integer> nextUpdater = AtomicReferenceFieldUpdater  
            .newUpdater(VolatileRunnable.class, Integer.class, "value"); // 增加一个AtomicReferenceFieldUpdater  
  
        private volatile Integer value = 0;  
  
        public void run() {  
            for (;;) {  
                if (value >= 10) {  
                    break;  
                }  
                // 递增value当前值  
                Integer current = value;  
                while (!nextUpdater.compareAndSet(this, current, current + 1)) { // 判断value值是否跟原值一样,一样则修改value值为current+1  
                    current = value;  
                }  
  
                System.out.println(Thread.currentThread().getName() + " " + value);  
            }  
        }  
    }  
  
    public static void main(String[] args) {  
        VolatileRunnable volatileRunnable = new VolatileRunnable();  
        Thread thread1 = new Thread(volatileRunnable);  
        Thread thread2 = new Thread(volatileRunnable);  
        Thread thread3 = new Thread(volatileRunnable);  
        Thread thread4 = new Thread(volatileRunnable);  
        thread1.start();  
        thread2.start();  
        thread3.start();  
        thread4.start();  
    }  
}  


这个例子会有重复。。
直接用volatile当前值变化,不能依赖上一个值。。
直接用static原子int就ok

相关推荐

Global site tag (gtag.js) - Google Analytics