摘要:如果兩個(gè)線(xiàn)程存取相同的對(duì)象,并且每一個(gè)線(xiàn)程都調(diào)用一個(gè)修改該對(duì)象狀態(tài)的方法,根據(jù)線(xiàn)程訪(fǎng)問(wèn)數(shù)據(jù)的順序,可能會(huì)出現(xiàn)錯(cuò)誤的數(shù)據(jù)結(jié)果,這種現(xiàn)象成為條件競(jìng)爭(zhēng)。而問(wèn)題往往就是有多個(gè)線(xiàn)程同時(shí)在執(zhí)行步驟。內(nèi)部鎖有如下的特點(diǎn)不能中斷正在試圖獲得鎖的線(xiàn)程。
【條件競(jìng)爭(zhēng)
在多線(xiàn)程的開(kāi)發(fā)中,兩個(gè)及其以上的線(xiàn)程需要共享統(tǒng)一數(shù)據(jù)的存取。如果兩個(gè)線(xiàn)程存取相同的對(duì)象,并且每一個(gè)線(xiàn)程都調(diào)用一個(gè)修改該對(duì)象狀態(tài)的方法,根據(jù)線(xiàn)程訪(fǎng)問(wèn)數(shù)據(jù)的順序,可能會(huì)出現(xiàn)錯(cuò)誤的數(shù)據(jù)結(jié)果,這種現(xiàn)象成為條件競(jìng)爭(zhēng)。因?yàn)樾薷膶?duì)象狀態(tài)的方法并不是一個(gè)原子操作,通常步驟是:
1. 讀取當(dāng)前狀態(tài)值到線(xiàn)程工作內(nèi)存。 2. 在線(xiàn)程工作內(nèi)存中修改狀態(tài)值。 3. 將修改后的狀態(tài)值重新寫(xiě)入主內(nèi)存。
而問(wèn)題往往就是有多個(gè)線(xiàn)程同時(shí)在執(zhí)行步驟2。
【 有兩種機(jī)制代碼受并發(fā)訪(fǎng)問(wèn)的干擾synchronized關(guān)鍵字。
Reentrantlock類(lèi)。
【synchronized關(guān)鍵字java中每個(gè)對(duì)象都有一個(gè)內(nèi)部鎖。如果一個(gè)方法是用synchronized關(guān)鍵字修聲明的,那么對(duì)象的鎖將保護(hù)整個(gè)方法,也就是說(shuō)要調(diào)用該方法,線(xiàn)程必須獲得對(duì)象內(nèi)部鎖。內(nèi)部鎖有如下的特點(diǎn):
不能中斷正在試圖獲得鎖的線(xiàn)程。
試圖獲得鎖不能設(shè)置超時(shí)時(shí)間。
只有一個(gè)條件:要么獲得,要么等待,沒(méi)有粒度更細(xì)的控制。
【多個(gè)線(xiàn)程修改同一個(gè)對(duì)象造成數(shù)據(jù)失誤public class Sync { private int value; void add(){ this.value ++; } int getValue(){ return this.value; } public static void main(String[] args) { for (int i = 0; i <30 ; i++) { Sync s = new Sync(); Listcfs = new ArrayList<>(); cfs.add(CompletableFuture.runAsync(() -> s.add())); cfs.add(CompletableFuture.runAsync(() -> s.add())); cfs.add(CompletableFuture.runAsync(() -> s.add())); //等待子線(xiàn)程執(zhí)行完畢 CompletableFuture.allOf(cfs.toArray(new CompletableFuture[cfs.size()])).join(); System.out.println(s.getValue()); } } }
現(xiàn)在使用synchronized關(guān)鍵字修飾add()看看:數(shù)據(jù)正常。
【注意如果我們?cè)谕降臅r(shí)候需要判斷,切記將條件判斷放在同步代碼塊之中,如果在外部判斷,很有可能出現(xiàn):
1. 第一個(gè)線(xiàn)程通過(guò)條件判斷 2. 第二個(gè)線(xiàn)程獲得cpu,并修改了共享對(duì)象 3. 第一個(gè)線(xiàn)程再次獲得cpu此時(shí)已經(jīng)不滿(mǎn)足條件,但是代碼在向下執(zhí)行,于是出現(xiàn)異常的情況。
例如轉(zhuǎn)賬的例子:
public class Account { //賬戶(hù)金額 private int amount; public Account(int amount){ this.amount = amount; } //轉(zhuǎn)賬 public synchronized void trans(Account to,int value){ //條件判斷處于同步代碼中?。。。。?! if(amount - value < 0){ throw new RuntimeException("錢(qián)不夠了!"); } this.amount -= value; to.addAmount(value); System.out.println("轉(zhuǎn)賬成功"); } public void addAmount(int amount){ this.amount += amount; } public int getAmount(){ return this.amount; } public static void main(String[] args) { Account from = new Account(100); Account to1 = new Account(0); Account to2 = new Account(0); Listcfs = new ArrayList<>(); cfs.add(CompletableFuture.runAsync(() -> from.trans(to1,100))); cfs.add(CompletableFuture.runAsync(() -> from.trans(to2,100))); CompletableFuture.allOf(cfs.toArray(new CompletableFuture[cfs.size()])).join(); System.out.println(from.getAmount()); System.out.println(to1.getAmount()); System.out.println(to2.getAmount()); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68094.html
摘要:假設(shè)不發(fā)生編譯器重排和指令重排,線(xiàn)程修改了的值,但是修改以后,的值可能還沒(méi)有寫(xiě)回到主存中,那么線(xiàn)程得到就是很自然的事了。同理,線(xiàn)程對(duì)于的賦值操作也可能沒(méi)有及時(shí)刷新到主存中。線(xiàn)程的最后操作與線(xiàn)程發(fā)現(xiàn)線(xiàn)程已經(jīng)結(jié)束同步。 很久沒(méi)更新文章了,對(duì)隔三差五過(guò)來(lái)刷更新的讀者說(shuō)聲抱歉。 關(guān)于 Java 并發(fā)也算是寫(xiě)了好幾篇文章了,本文將介紹一些比較基礎(chǔ)的內(nèi)容,注意,閱讀本文需要一定的并發(fā)基礎(chǔ)。 本文的...
摘要:介紹中無(wú)鎖的線(xiàn)程安全整數(shù),一個(gè)提供原子操作的的類(lèi)。在語(yǔ)言中,和操作并不是線(xiàn)程安全的,在使用的時(shí)候,不可避免的會(huì)用到關(guān)鍵字。而則通過(guò)一種線(xiàn)程安全的加減操作接口。就是的意思,比較并操作。有個(gè)操作數(shù),內(nèi)存值,舊的預(yù)期值,要修改的新值。 【介紹 JAVA 中無(wú)鎖的線(xiàn)程安全整數(shù) AtomicInteger,一個(gè)提供原子操作的Integer的類(lèi)。在Java語(yǔ)言中,++i和i++操作并不是線(xiàn)程安全的...
摘要:死亡狀態(tài)線(xiàn)程退出有可能是正常執(zhí)行完成也有可能遇見(jiàn)異常退出。類(lèi)有新建與死亡狀態(tài)返回其余狀態(tài)返回判斷線(xiàn)程是否存活。線(xiàn)程因某些原因進(jìn)入阻塞狀態(tài)。執(zhí)行同步代碼塊的過(guò)程中執(zhí)行了當(dāng)前線(xiàn)程放棄開(kāi)始睡眠進(jìn)入就緒狀態(tài)但是不會(huì)釋放鎖。 【java內(nèi)存模型簡(jiǎn)介 JVM中存在一個(gè)主存區(qū)(Main Memory或Java Heap Memory),Java中所有變量都是存在主存中的,對(duì)于所有線(xiàn)程進(jìn)行共享,而每個(gè)...
摘要:三關(guān)鍵字能保證原子性嗎并發(fā)編程藝術(shù)這本書(shū)上說(shuō)保證但是在自增操作非原子操作上不保證,多線(xiàn)程編程核心藝術(shù)這本書(shū)說(shuō)不保證。多線(xiàn)程訪(fǎng)問(wèn)關(guān)鍵字不會(huì)發(fā)生阻塞,而關(guān)鍵字可能會(huì)發(fā)生阻塞關(guān)鍵字能保證數(shù)據(jù)的可見(jiàn)性,但不能保證數(shù)據(jù)的原子性。 系列文章傳送門(mén): Java多線(xiàn)程學(xué)習(xí)(一)Java多線(xiàn)程入門(mén) Java多線(xiàn)程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線(xiàn)程學(xué)習(xí)(二)synchroniz...
摘要:相比與其他操作系統(tǒng)包括其他類(lèi)系統(tǒng)有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。因?yàn)槎嗑€(xiàn)程競(jìng)爭(zhēng)鎖時(shí)會(huì)引起上下文切換。減少線(xiàn)程的使用。很多編程語(yǔ)言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時(shí)至關(guān)重要。 系列文章傳送門(mén): Java多線(xiàn)程學(xué)習(xí)(一)Java多線(xiàn)程入門(mén) Java多線(xiàn)程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線(xiàn)程學(xué)習(xí)(二)syn...
閱讀 2256·2023-04-26 00:00
閱讀 3456·2021-09-24 10:37
閱讀 3626·2021-09-07 09:58
閱讀 1588·2019-08-30 15:56
閱讀 2275·2019-08-30 13:11
閱讀 2370·2019-08-29 16:38
閱讀 1060·2019-08-29 12:58
閱讀 1985·2019-08-27 10:54