java中的Volatile关键字使用
文章目錄
- 什么時候使用volatile
- Happens-Before
java中的Volatile關鍵字使用
在本文中,我們會介紹java中的一個關鍵字volatile。 volatile的中文意思是易揮發的,不穩定的。那么在java中使用是什么意思呢?
我們知道,在java中,每個線程都會有個自己的內存空間,我們稱之為working memory。這個空間會緩存一些變量的信息,從而提升程序的性能。當執行完某個操作之后,thread會將更新后的變量更新到主緩存中,以供其他線程讀寫。
因為變量存在working memory和main memory兩個地方,那么就有可能出現不一致的情況。 那么我們就可以使用Volatile關鍵字來強制將變量直接寫到main memory,從而保證了不同線程讀寫到的是同一個變量。
什么時候使用volatile
那么我們什么時候使用volatile呢?當一個線程需要立刻讀取到另外一個線程修改的變量值的時候,我們就可以使用volatile。我們來舉個例子:
public class VolatileWithoutUsage {private int count = 0;public void incrementCount() {count++;}public int getCount() {return count;} }這個類定義了一個incrementCount()方法,會去更新count值,我們接下來在多線程環境中去測試這個方法:
@Testpublic 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,我們再測試一下:
@Testpublic 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;} } @Testpublic 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一樣。
本文的例子可以參考:https://github.com/ddean2009/learn-java-concurrency/tree/master/volatile
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
更多教程請參考 flydean的博客
總結
以上是生活随笔為你收集整理的java中的Volatile关键字使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot JPA 中tra
- 下一篇: java中wait和sleep的区别