java关键字有很多,而volatile是在处理java线程中经常使用的,很多新手不清楚java关键字volatile怎么用?那下面我们就给大家讲解一下java关键字volatile的含义及使用方法。
volatile 是一个关键字,用于修饰成员变量。
java关键字volatile使用:
当一个线程需要立刻读取到另外一个线程修改的变量值的时候,我们就可以使用volatile。我们来举个例子:
public class VolatileWithoutUsage { private int count = 0; public void incrementCount() { count++; } public int getCount() { return count; } }
这个类定义了一个incrementCount()方法,会去更新count值,我们接下来在多线程环境中去测试这个方法:
@Test public void testWithoutVolatile() throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(3); VolatileWithoutUsage volatileWithoutUsage = new VolatileWithoutUsage(); IntStream.range(0, 1000) .forEach(count - > service.submit(volatileWithoutUsage::incrementCount)); service.shutdown(); service.awaitTermination(1000, TimeUnit.MILLISECONDS); assertEquals(1000, volatileWithoutUsage.getCount()); }
运行一下,我们会发现结果是不等于1000的。
java.lang.AssertionError: Expected: 1000 Actual: 999
这是因为多线程去更新同一个变量,我们在上篇文章也提到了,这种情况可以通过加Synchronized关键字来解决。
那么是不是我们加上Volatile关键字后就可以解决这个问题了呢?
public class VolatileFalseUsage { private volatile int count = 0; public void incrementCount() { count++; } public int getCount() { return count; } }
上面的类中,我们加上了关键字Volatile,我们再测试一下:
@Test public void testWithVolatileFalseUsage() throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(3); VolatileFalseUsage volatileFalseUsage = new VolatileFalseUsage(); IntStream.range(0, 1000) .forEach(count - > service.submit(volatileFalseUsage::incrementCount)); service.shutdown(); service.awaitTermination(5000, TimeUnit.MILLISECONDS); assertEquals(1000, volatileFalseUsage.getCount()); }
运行一下,我们会发现结果还是错误的:
java.lang.AssertionError: Expected :1000 Actual :992 ~~ 为什么呢? 我们先来看下count++的操作,count++可以分解为三步操作,1. 读取count的值,2.给count加1, 3.将count写回内存。添加Volatile关键词只能够保证count的变化立马可见,而不能保证1,2,3这三个步骤的总体原子性。 要实现总体的原子性还是需要用到类似Synchronized的关键字。 下面看下正确的用法: ~~~java public class VolatileTrueUsage { private volatile int count = 0; public void setCount(int number) { count=number; } public int getCount() { return count; } }
@Test @Test public void testWithVolatileTrueUsage() throws InterruptedException { VolatileTrueUsage volatileTrueUsage = new VolatileTrueUsage(); Thread threadA = new Thread(() - > volatileTrueUsage.setCount(10)); threadA.start(); Thread.sleep(100); Thread reader = new Thread(() - > { int valueReadByThread = volatileTrueUsage.getCount(); assertEquals(10, valueReadByThread); }); reader.start(); }
Happens-Before
从java5之后,volatile提供了一个Happens-Before的功能。Happens-Before 是指当volatile进行写回主内存的操作时,会将之前的非volatile的操作一并写回主内存。
public class VolatileHappenBeforeUsage { int a = 0; volatile boolean flag = false; public void writer() { a = 1; // 1 线程A修改共享变量 flag = true; // 2 线程A写volatile变量 } }
上面的例子中,a是一个非volatile变量,flag是一个volatile变量,但是由于happens-before的特性,a 将会表现的和volatile一样。
其实volatile关键字的作用是保证变量在多线程之间的可见性,同时禁止进行指令重排序,所以这样看来volatile关键字还是很重要的!最后大家如果想要了解更多java入门知识,敬请关注奇Q工具网。
推荐阅读: