您的位置:首页 > 编程语言 > Java开发

java中volatile关键字---学习笔记

2016-06-30 14:31 351 查看
volatile关键字的作用

在java内存模型中,线程之间共享堆内存(对应主内存),但又各自拥有自己的本地内存——栈内存,线程的栈内存中缓存有共享变量的副本,但如果是被volatile修饰的变量,线程每次都直接从堆内存中读取最新值,并在操作完成时将新值写入堆内存。

但需要注意的一点是:volatile关键字只能保证主存中的变量值是最新的,并不能保证操作的原子性,因此它不能代替synchronized。像简单的i++也不是原子操作,它包括了read(i),inc(i),write(i)的过程,可能:当一个线程刚从主存中读取出i的最新值还未进行下一步操作时,另一个线程正往主存中写入新值,此时就会出现线程不安全的情况。

什么是线程安全?

通俗的解释就是:有多个线程在同时运行一段代码,如果每次运行的结果和单线程运行的结果一样,而且其他的变量值也和预期的一样,这样叫做线程安全。例子:一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。

volatile拥有可见性但是不具有原子性的验证

这里通过创建1000个线程对同一个计数器Counter进行++操作来验证volatile的线程安全性。为了排除main thread最后读取count值时,尚有线程没有结束的情况,这里引入 Java concurrent包里面的一个同步辅助类CountDownLatch,通过countDown()方法和await()方法来保证main thread最后读取到的count值是主存中最新的。

CountDownLatch latch = new CountDownLatch(1000); //用给定的计数次数1000进行初始化。

latch.countDown(); //此操作是原子操作,即同时只允许有一个线程去减这个计数器里面的值。

latch.await();//在计数器latch里面的值变为0之前一直处于阻塞状态。

import java.util.concurrent.CountDownLatch;

public class Counter {

public volatile static int count = 0;
CountDownLatch latch = new CountDownLatch(1000);

public static void inc() {
count++;
}

//1000 threads start simultaneously for i ++ calculations, to see the actual results ;
public void test() throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
}).start();
latch.countDown(); //Ensure that all threads have been completed ;
System.out.println("Come here " + (i + 1) + " times");
}
latch.await(); //Until 1000 threads complete before reading the counter value inside ;
System.out.println(Thread.currentThread().getName() + " thread : " + Counter.count);
}
//Here the value of each run are likely to differ , possibly 1000 ;

public static void main(String[] args) {

try {
new Counter().test();
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}


运行结果:



测试结果说明:java中的volatile关键字只可保证读取可见性但并不是线程安全的,在实际使用过程中,我们要具体情况具体分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: