java关键字volatile怎么用?java关键字volatile的含义及使用

java关键字有很多,而volatile是在处理java线程中经常使用的,很多新手不清楚java关键字volatile怎么用?那下面我们就给大家讲解一下java关键字volatile的含义及使用方法。

java关键字volatile的含义及使用.jpg

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工具网。

推荐阅读:

qt安装组件能全选吗?qt安装过程会出现哪些问题?

java编译方式怎么查?Java有几种编译方式?

javaIDEA怎么写bat文件?javaIDEA项目如何创建?