Java內(nèi)存模型原子性原理及實(shí)例解析
這篇文章主要介紹了Java內(nèi)存模型原子性原理及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
本文就具體來(lái)講講JMM是如何保證共享變量訪問(wèn)的原子性的。
原子性問(wèn)題
原子性是指:一個(gè)或多個(gè)操作,要么全部執(zhí)行且在執(zhí)行過(guò)程中不被任何因素打斷,要么全部不執(zhí)行。
下面就是一段會(huì)出現(xiàn)原子性問(wèn)題的代碼:
public class AtomicProblem { private static Logger logger = LoggerFactory.getLogger(AtomicProblem.class); public static final int THREAD_COUNT = 10; public static void main(String[] args) throws Exception { BankAccount sharedAccount = new BankAccount("account-csx",0.00); ArrayList<Thread> threads = new ArrayList<>(); for (int i = 0; i < THREAD_COUNT; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 1000 ; j++) { sharedAccount.deposit(10.00); } } }); thread.start(); threads.add(thread); } for (Thread thread : threads) { thread.join(); } logger.info("the balance is:{}",sharedAccount.getBalance()); } public static class BankAccount { private String accountName; public double getBalance() { return balance; } private double balance; public BankAccount(String accountName, double balance){ this.accountName = accountName; this.balance =balance; } public double deposit(double amount){ balance = balance + amount; return balance; } public double withdraw(double amount){ balance = balance - amount; return balance; } public String getAccountName() { return accountName; } public void setAccountName(String accountName) { this.accountName = accountName; } } }
上面的代碼中開(kāi)啟了10個(gè)線程,每個(gè)線程會(huì)對(duì)共享的銀行賬戶進(jìn)行1000次存款操作,每次存款10塊,所以理論上最后銀行賬戶中的錢(qián)應(yīng)該是10 * 1000 * 10 = 100000塊。我執(zhí)行了多次上面的代碼,很多次最后的結(jié)果的確是100000,但是也有幾次的結(jié)果并不是我們預(yù)期的。
14:40:25.981 [main] INFO com.csx.demo.spring.boot.concurrent.jmm.AtomicProblem - the balance is:98260.0
出現(xiàn)上面結(jié)果的原因就是因?yàn)橄旅娴牟僮鞑⒉皇窃硬僮?,其中的balance是一個(gè)共享變量。在多線程環(huán)境下可能會(huì)被打斷。
balance = balance + amount;
上面的賦值操作被分為多步執(zhí)行完成,下面簡(jiǎn)單解析下兩個(gè)線程對(duì)balance同時(shí)加10的過(guò)程(模擬存款過(guò)程,假設(shè)balance的初始值還是0)
線程1從共享內(nèi)存中加載balance的初始值0到工作內(nèi)存 線程1對(duì)工作內(nèi)存中的值加10 //此時(shí)線程1的CPU時(shí)間耗盡,線程2獲得執(zhí)行機(jī)會(huì) 線程2從共享內(nèi)存中加載balance的初始值到工作內(nèi)存,此時(shí)balance的值還是0 線程2對(duì)工作內(nèi)存中的值加10,此時(shí)線程2工作內(nèi)存中的副本值是10 線程2將balance的副本值刷新回共享內(nèi)存,此時(shí)共享內(nèi)存中balance的值是10 //線程2CPU時(shí)間片耗盡,線程1又獲得執(zhí)行機(jī)會(huì) 線程1將工作內(nèi)存中的副本值刷新回共享內(nèi)存,但是此時(shí)副本的值還是10,所以最后共享內(nèi)存中的值也是10
上面簡(jiǎn)單模擬了一個(gè)原子性問(wèn)題導(dǎo)致程序最終結(jié)果出錯(cuò)的過(guò)程。
JMM對(duì)原子性問(wèn)題的保證
自帶原子性保證
在Java中,對(duì)基本數(shù)據(jù)類(lèi)型的變量的讀取和賦值操作是原子性操作。
a = true; //原子性 a = 5; //原子性 a = b; //非原子性,分兩步完成,第一步加載b的值,第二步將b賦值給a a = b + 2; //非原子性,分三步完成 a ++; //非原子性,分三步完成
synchronized
synchronized可以保證操作結(jié)果的原子性。synchronized保證原子性的原理也很簡(jiǎn)單,因?yàn)閟ynchronized可以防止多個(gè)線程并發(fā)執(zhí)行一段代碼。還是用上面存款的場(chǎng)景做列子,我們只需要將存款的方法設(shè)置成synchronized的就能保證原子性了。
public synchronized double deposit(double amount){ balance = balance + amount; //1 return balance; }
加了synchronized后,當(dāng)一個(gè)線程沒(méi)執(zhí)行完deposit這個(gè)方法前,其他線程是不能執(zhí)行這段代碼的。其實(shí)我們發(fā)現(xiàn)synchronized并不能將上面的代碼1編程原子性操作,上面的代碼1還是有可能被中斷的,但是即使被中斷了其他線程也不能訪問(wèn)共享變量balance,當(dāng)之前被中斷的線程繼續(xù)執(zhí)行時(shí)得到的結(jié)果還是正確的。
因此synchronized對(duì)原子性問(wèn)題的保證是從最終結(jié)果上來(lái)保證的,也就是說(shuō)它只保證最終的結(jié)果正確,中間操作的是否被打斷沒(méi)法保證。這個(gè)和CAS操作需要對(duì)比著看。
Lock鎖
public double deposit(double amount) { readWriteLock.writeLock().lock(); try { balance = balance + amount; return balance; } finally { readWriteLock.writeLock().unlock(); } }
Lock鎖保證原子性的原理和synchronized類(lèi)似,這邊不進(jìn)行贅述了。
原子操作類(lèi)型
public static class BankAccount { //省略其他代碼 private AtomicDouble balance; public double deposit(double amount) { return balance.addAndGet(amount); } //省略其他代碼 }
JDK提供了很多原子操作類(lèi)來(lái)保證操作的原子性。原子操作類(lèi)的底層是使用CAS機(jī)制的,這個(gè)機(jī)制對(duì)原子性的保證和synchronized有本質(zhì)的區(qū)別。CAS機(jī)制保證了整個(gè)賦值操作是原子的不能被打斷的,而synchronized值能保證代碼最后執(zhí)行結(jié)果的正確性,也就是說(shuō)synchronized能消除原子性問(wèn)題對(duì)代碼最后執(zhí)行結(jié)果的影響。
簡(jiǎn)單總結(jié)
在多線程編程環(huán)境下(無(wú)論是多核CPU還是單核CPU),對(duì)共享變量的訪問(wèn)存在原子性問(wèn)題。這個(gè)問(wèn)題可能會(huì)導(dǎo)致程序錯(cuò)誤的執(zhí)行結(jié)果。JMM主要提供了如下的方式來(lái)保證操作的原子,保證程序不受原子性問(wèn)題的影響。
- synchronized機(jī)制:保證程序最終正確性,是的程序不受原子性問(wèn)題的影響;
- Lock接口:和synchronized類(lèi)似;
- 原子操作類(lèi):底層使用CAS機(jī)制,能保證操作真正的原子性。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:springboot 配置DRUID數(shù)據(jù)源的方法實(shí)例分析
欄 目:Java
下一篇:如何基于LoadingCache實(shí)現(xiàn)Java本地緩存
本文標(biāo)題:Java內(nèi)存模型原子性原理及實(shí)例解析
本文地址:http://mengdiqiu.com.cn/a1/Java/8904.html
您可能感興趣的文章
- 01-10Java實(shí)現(xiàn)動(dòng)態(tài)模擬時(shí)鐘
- 01-10利用Java實(shí)現(xiàn)復(fù)制Excel工作表功能
- 01-10JavaWeb實(shí)現(xiàn)郵件發(fā)送功能
- 01-10java基于poi導(dǎo)出excel透視表代碼實(shí)例
- 01-10Java實(shí)現(xiàn)動(dòng)態(tài)數(shù)字時(shí)鐘
- 01-10基于Java驗(yàn)證jwt token代碼實(shí)例
- 01-10java實(shí)現(xiàn)液晶數(shù)字字體顯示當(dāng)前時(shí)間
- 01-10淺談Java中真的只有值傳遞么
- 01-10Java動(dòng)態(tài)顯示當(dāng)前日期和時(shí)間
- 01-10如何解決線程太多導(dǎo)致java socket連接池出現(xiàn)的問(wèn)題


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10Java實(shí)現(xiàn)動(dòng)態(tài)模擬時(shí)鐘
- 01-10Springboot中@Value的使用詳解
- 01-10JavaWeb實(shí)現(xiàn)郵件發(fā)送功能
- 01-10利用Java實(shí)現(xiàn)復(fù)制Excel工作表功能
- 01-10Java實(shí)現(xiàn)動(dòng)態(tài)數(shù)字時(shí)鐘
- 01-10java基于poi導(dǎo)出excel透視表代碼實(shí)例
- 01-10java實(shí)現(xiàn)液晶數(shù)字字體顯示當(dāng)前時(shí)間
- 01-10基于Java驗(yàn)證jwt token代碼實(shí)例
- 01-10Java動(dòng)態(tài)顯示當(dāng)前日期和時(shí)間
- 01-10淺談Java中真的只有值傳遞么
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?