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