摘要:每個對象只有一個鎖與之相關聯(lián)。實現(xiàn)同步則是以系統(tǒng)開銷作為代價,甚至可能造成死鎖,所以盡量避免濫用。這種機制確保了同一時刻該類實例,所有聲明為的函數(shù)中只有一個方法處于可執(zhí)行狀態(tài),從而有效避免了類成員變量訪問沖突。
synchronized是JAVA語言的一個關鍵字,使用 synchronized 來修飾方法或代碼塊的時候,能夠保證多個線程中最多只有一個線程執(zhí)行該段代碼 ...概述
synchronized關鍵字可以作為函數(shù)的修飾符,也可作為函數(shù)內的語句,也就同步方法和同步代碼塊塊。細分為 instance variable(實例變量)、object reference(對象引用)、static method(靜態(tài)方法) 和 class literals(常量類)。
無論·synchronized·關鍵字加在方法上還是對象上,它獲取的都是對象鎖,而不是將一段代碼或一個函數(shù)當作鎖,而且同步方法很可能還會被其他線程的對象訪問。
每個對象只有一個鎖(lock)與之相關聯(lián)。
實現(xiàn)同步則是以系統(tǒng)開銷作為代價,甚至可能造成死鎖,所以盡量避免濫用。
同步方法: 使用 synchronized 標記的方法,只有獲得該方法類實例的鎖才能執(zhí)行,否則所屬線程將被阻塞,方法一旦執(zhí)行,就獨占該鎖,直到該方法執(zhí)行完畢將鎖釋放,被阻塞的線程才能獲得鎖從而執(zhí)行。這種機制確保了同一時刻該類實例,所有聲明為 synchronized 的函數(shù)中只有一個方法處于可執(zhí)行狀態(tài),從而有效避免了類成員變量訪問沖突。
同步方法缺陷:若將一個大的方法聲明為 synchronized 將會大大的影響效率,典型的,若將線程類的方法 run() 聲明為 synchronized,由于在線程的整個生命期中它一直在運行,因此將導致對本類任何 synchronized 方法的調用都不會成功。因此在這種環(huán)境下,可以使用同步代碼塊的方式
同步代碼塊: 除了方法前用synchronized關鍵字,還可以用于方法中的某個區(qū)塊中,表示只對該區(qū)域內的資源進行互斥操作。用法是: synchronized(this){/區(qū)塊/},它的作用域是當前對象。也可以創(chuàng)建一個特殊的instance變量(它得是一個對象)來充當鎖
寫法類的范圍寫法,防止多個線程同時訪問這個類中的synchronized method,它可以對類的所有對象實例起作用
static synchronized void transferAccount() { //... } //等同 static void transferAccount() { synchronized(Bank.class) { //... } }
對象實例內寫法,多個線程同時訪問該對象的synchronized 方法,如果該對象實例有多個synchronized方法,任意線程訪問了其中的一個synchronized方法,剩余線程則不能并發(fā)訪問該對象中任何一個synchronized方法(不同對象實例的 synchronized方法是互不干擾的。其它線程依然可以并發(fā)訪問相同對象類不同實例中的synchronized方法,如果想做到在不同對象實例同步需要使用class literal的方式)
synchronized void transferAccount() { //... } void transferAccount() { synchronized(this) { //... } } private final byte[] LOCK = new byte[0]; // 特殊的實例化對象 void transferAccount() { synchronized(LOCK) { //... } }案例一:靜態(tài)同步方法
class Bank1 { synchronized static void transferAccount() { System.out.println("開始轉賬:" + Thread.currentThread().getName()); try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("轉賬完畢"); } synchronized static void debit() { System.out.println("開始扣款:" + Thread.currentThread().getName()); try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("扣款完畢"); } } public class BankMain { public static void main(String[] args) { new Thread(Bank1::transferAccount, "北京銀行").start(); new Thread(Bank1::debit, "上海銀行").start(); } } ////////////////////////日志//////////////////////// 開始轉賬:北京銀行 轉賬完畢 開始扣款:上海銀行 扣款完畢 ////////////////////////日志////////////////////////
分析:通過日志看到在使用synchronized后,雖然是調用的不同方法,但是線程還是同步去執(zhí)行的(不加并發(fā)執(zhí)行,結果你懂得(^▽^))
案例二:同步方法單一對象鎖class Bank2 implements Runnable { @Override public synchronized void run() { System.out.println("查詢數(shù)據(jù):" + Thread.currentThread().getName()); System.out.println("開始轉賬:" + Thread.currentThread().getName()); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("轉賬完畢"); } } public class BankMain { public static void main(String[] args) { Bank2 bank2 = new Bank2(); new Thread(bank2, "北京銀行").start(); new Thread(bank2, "上海銀行").start(); } } ////////////////////////日志//////////////////////// 查詢數(shù)據(jù):北京銀行 開始轉賬:北京銀行 轉賬完畢 查詢數(shù)據(jù):上海銀行 開始轉賬:上海銀行 轉賬完畢 ////////////////////////日志////////////////////////
分析:方法同步執(zhí)行,誰獲得鎖誰先執(zhí)行
案例三:Lock對象鎖class Bank3 implements Runnable { private final byte[] LOCK = new byte[0]; // 特殊的實例化變量 @Override public void run() { System.out.println("查詢數(shù)據(jù):" + Thread.currentThread().getName()); synchronized (LOCK) {//該種方式只能鎖 System.out.println("開始轉賬:" + Thread.currentThread().getName()); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("轉賬完畢"); } } } public class BankMain { public static void main(String[] args) { Bank3 bank = new Bank3(); new Thread(bank, "北京銀行").start(); new Thread(bank, "上海銀行").start(); } } ////////////////////////日志//////////////////////// 查詢數(shù)據(jù):北京銀行 查詢數(shù)據(jù):上海銀行 開始轉賬:北京銀行 轉賬完畢 開始轉賬:上海銀行 轉賬完畢 ////////////////////////日志////////////////////////
分析:互斥部分上鎖,查詢數(shù)據(jù)部分則并發(fā)執(zhí)行
案例四:同步到多個對象鎖前文說過一個實例對象一把鎖,在案例三與案例四中,都只實例化了一個對象,當對象為多實例化的時候,需使用class literal 的方式,它和synchronized static method方式產生的結果一樣,取得的鎖很特別,為當前調用該方法對象所屬的類(而不再是由這個Class產生的某個具體對象了)。
class Bank4 implements Runnable { @Override public void run() { System.out.println("查詢數(shù)據(jù):" + Thread.currentThread().getName()); synchronized (Bank4.class) { System.out.println("開始轉賬:" + Thread.currentThread().getName()); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("轉賬完畢"); } } } public class BankMain { public static void main(String[] args) { new Thread(new Bank4(), "北京銀行").start(); new Thread(new Bank4(), "上海銀行").start(); } } ////////////////////////日志//////////////////////// 查詢數(shù)據(jù):北京銀行 查詢數(shù)據(jù):上海銀行 開始轉賬:北京銀行 轉賬完畢 開始轉賬:上海銀行 轉賬完畢 ////////////////////////日志////////////////////////
可以推斷:如果一個類中定義了一個synchronized static methodA,也定義了一個 synchronized 的 instance methodB,該類同一個對象在多線程中分別訪問A和B兩個方法時,并不會構成同步,因為它們的鎖都不一樣。methodA的鎖是它的所屬Class,而methodB的鎖是當前對象(該部分代碼未貼出,可以自己實現(xiàn)或者看GIT)
- 說點什么全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter5
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67629.html
摘要:一般差異簡單來說,是一個用于線程同步的實例方法。暫停當前線程,不釋放任何鎖。用來線程間通信,使擁有該對象鎖的線程等待直到指定時間或。執(zhí)行對該對象加的同步代碼塊。 在JAVA的學習中,不少人會把sleep和wait搞混,認為都是做線程的等待,下面主要介紹下這倆者是什么,及了解它們之間的差異和相似之處。 一般差異 簡單來說,wait()是一個用于線程同步的實例方法。因為定義在java.l...
摘要:上一章介紹過關鍵字,使用它可以給程序互斥部分加上一把鎖從而達到同步的效果,但錯誤的用法會導致多個線程同時被阻塞死鎖死鎖多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,因此程序不可能正常終止。 上一章介紹過synchronized關鍵字,使用它可以給程序互斥部分加上一把鎖從而達到同步的效果,但錯誤的用法會導致多個線程同時被阻塞.... 死鎖 死鎖...
摘要:如果有其它線程調用了相同對象的方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,從新爭奪鎖的擁有權。 wait,notify 和 notifyAll,這些在多線程中被經常用到的保留關鍵字,在實際開發(fā)的時候很多時候卻并沒有被大家重視,而本文則是對這些關鍵字的使用進行描述。 存在即合理 在java中,每個對象都有兩個池,鎖池(monitor)和等待池(waitset),每個...
摘要:學習完多線程之后可以通過下面這些問題檢測自己是否掌握,下面這些問題的答案以及常見多線程知識點的總結在這里??蛇x數(shù)據(jù)結構與算法如果你想進入大廠的話,我推薦你在學習完基礎或者多線程之后,就開始每天抽出一點時間來學習算法和數(shù)據(jù)結構。 我自己總結的Java學習的系統(tǒng)知識點以及面試問題,已經開源,目前已經 35k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://...
摘要:比如用修飾的變量,就會確保變量在修改時,其它線程是可見的。。多核環(huán)境中,多個線程分別在不同的中運行,就意味著,多個線程都有可能將變量拷貝到當前運行的里。當線程讀取變量時,它將能看見被線程寫入的東西。 volatile是用來標記一個JAVA變量存儲在主內存(main memory)中,多線程讀寫volatile變量會先從高速緩存中讀取,但是寫入的時候會立即通過內存總線刷到主存,同時內存總...
閱讀 1963·2023-04-26 01:59
閱讀 3271·2021-10-11 11:07
閱讀 3300·2021-09-22 15:43
閱讀 3381·2021-09-02 15:21
閱讀 2557·2021-09-01 10:49
閱讀 906·2019-08-29 15:15
閱讀 3093·2019-08-29 13:59
閱讀 2835·2019-08-26 13:36