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