synchronized關(guān)鍵字采用對代碼塊/方法體加鎖的方式解決Java中多線程訪問同一個資源時针饥,引起的資源沖突問題洲守。
synchronized 同步鎖可分為兩種類型,四種表現(xiàn)形式
-
對象鎖: 對單個實例對象的獨享內(nèi)存的部分區(qū)域加鎖
- 修飾非靜態(tài)方法
- 修飾代碼塊
-
類鎖: 對整個類的共享內(nèi)存的部分區(qū)域加鎖
- 修飾靜態(tài)方法
- 修飾代碼塊
Find Two Object
沃茲吉索得: 面向?qū)ο缶幊痰木柙谟谫N合實際眷射,更容易建立易于理解的對象模型挖腰。比如設(shè)計模式用于面向?qū)ο蟮木幊堂黠@更加易于理解雕沿。 :)
- 首先我們定義一個Human類:
public class Human {
//所有的Human都有吃晚飯這個動作
public void eatDinner() {
pickupByLeftHand(); //用左手拿起食物
eatFoodByMouth(); //用嘴吃食物
};
//所有的Human都有喝水這個動作
public void drinkWater() {
pickupByRightHand(); //用右手拿起水
drinkWaterByMouth(); //用嘴喝水
}
//看電視
public void watchTV() {
}
}
- 然后我們實例化兩個對象:
Human XiaoMing = new Human(); //小明
Human XiaoMingTaBa = new Human(); //小明他爸
Let's have dinner!
小明要吃晚飯了 XiaoMing.eatDinner()
, 忽然他又想喝水了 XiaoMing.drinkWater()
曙聂。好吧晦炊,他只有一張嘴鞠鲜,同時又喝水又吃飯會被嗆到吧宁脊?這時我們想了個解決辦法:
//使用 synchronized 關(guān)鍵字修飾 eatDinner()
public synchronized void eatDinner() {
pickupByHand(); //用手拿起食物
eatFoodByMouth(); //用嘴吃食物
}
//使用 synchronized 關(guān)鍵字修飾 drinkWater
public synchronized void eatDinner() {
pickupByHand(); //用手拿起水
drinkWaterByMouth(); //用嘴喝水
}
現(xiàn)在當小明在吃飯的時候,他想喝水了贤姆,由于我們使用了synchronized修飾這兩個動作榆苞。小明在喝水之前會先把飯吃完再執(zhí)行喝水的動作。這樣就解決了沖突的問題霞捡。
再來分析下watchTV()這個動作:由于這個動作和吃晚飯喝水并沒有沖突可以同時進行坐漏,所以我們并不需要synchronized關(guān)鍵字來對它進行修飾。
當我們調(diào)用synchronized修飾的非靜態(tài)方法時碧信,所有采用了synchronized修飾的同步方法都會被鎖定赊琳,所有沒有采用synchronized修飾的非同步方法仍然可以執(zhí)行。
現(xiàn)在我們來探討一下小明他爸吃飯喝水的問題:雖然我們對小明的eatDinner()方法和drinkWater()方法進行了同步鎖定砰碴,但是顯而易見的是躏筏。小明喝水吃飯的動作并不影響小明他爸喝水吃飯的動作。所以我們可以對上面的結(jié)論進行完善:
Conclusion1: 使用 synchronized修飾非靜態(tài)方法
當我們通過某個實例對象A(XiaoMing)訪問非靜態(tài)同步方法(eat or drink)時呈枉,所有通過對象A訪問非靜態(tài)同步方法(eat or drink)的操作都會被阻塞直至另一個操作執(zhí)行完畢趁尼,而所有通過對象A訪問非靜態(tài)同步方法的操作(watchTV)仍然可以執(zhí)行。而當我們通過對象A訪問某個同步方法時猖辫,仍然可以通過對象B訪問任意一個同步方法酥泞。(即小明吃飯喝水不影響小明他爸吃飯喝水)
Let's have dinner 再來一次!
現(xiàn)在我們來分析另一種吃飯喝水的情況啃憎。小明和小明他爸一起在吃飯芝囤,兩個人都想喝水了,但是桌子上這時只有1瓶水了,這瓶水兩個人誰先搶到誰就能喝悯姊。小明的經(jīng)驗告訴他吃飯的同時如果喝水的話會被嗆到名党,所以他只好先把飯吃完再去喝水。而小明他爸吃過的鹽比小明吃過的米還多, 經(jīng)驗豐富的他知道吃飯時喝水會被嗆到是因為我們只有一張嘴,可是我們有兩只手啊挠轴。所以他的吃飯和喝水的動作是這樣實現(xiàn)的:
public void eatDinner() {
pickupByLeftHand(); //用手拿起食物
synchronized (mouth) {
eatFoodByMouth(); //用嘴吃食物
}
};
public void drinkWater() {
pickupByRightHand(); //用手拿起水
synchronized (mouth) {
drinkWaterByMouth(); //用嘴喝水
}
}
所以他在吃飯的同時传睹,用手拿起了水。好吧岸晦,雖然小明和小明他爸吃飯速度差不多欧啤,但是當大家都吃完飯的時候水已經(jīng)在小明他爸手里了。启上。邢隧。。(小明目瞪口呆.jpg)
總的吃飯時間計時是這樣的:(如果不存在競爭關(guān)系)
小明:timeOf(用右手拿起食物) + timeOf(用嘴吃食物) + timeOf(用左手拿起水) + timeOf(喝水)
小明他爸:imeOf(用右手拿起食物) + timeOf(用嘴吃食物冈在,同時可以用左手拿起水倒慧,而不增加吃食物的時間) + timeOf(喝水)
所以又得到了一個結(jié)論
**Conclusion2: **對象鎖的兩種形式的區(qū)別
這兩種形式?jīng)]有本質(zhì)上的不同。區(qū)別在于修飾代碼塊的形式盡可能的精簡了需要鎖住的同步代碼包券,使的我們的系統(tǒng)在高并發(fā)纫谅、資源競爭激烈的情況下更加高效。
關(guān)于類鎖
好吧溅固,我想不出類比的例子了付秕。直接說結(jié)論
Conclusion3: 類鎖(synchronized修飾靜態(tài)方法,synchronized(Human.class)修飾代碼塊)
訪問這兩種方式修飾的操作時侍郭,所有其他的采用synchronized修飾的靜態(tài)方法和采用synchronized(Human.this)修飾的代碼塊都會被鎖定(靜態(tài)的非同步方法不會被鎖定)询吴。但是不會影響到對象鎖,文章開頭有說明兩種鎖鎖住的內(nèi)存區(qū)域是不同的亮元!
Conclusion4: 類鎖的兩種形式的區(qū)別
參考對象鎖兩種形式的區(qū)別
The Last Supper
關(guān)于synchronized的兩種類型猛计,四種形式我們可以得出以下結(jié)論
- 當一個線程通過A對象訪問一個非靜態(tài)的同步方法(或同步代碼塊)時,其他線程通過A對象訪問非靜態(tài)同步方法(或同步代碼塊)的操作都會被鎖定爆捞。但是其他線程通過A對象訪問非靜態(tài)非同步方法的操作不會被鎖定奉瘤,而且通過B對象訪問非靜態(tài)同步方法的操作也不會被鎖。(因為各個對象的鎖是獨立的所以叫做對象鎖)
- 當一個線程訪問靜態(tài)同步方法或者由類鎖修飾的同步代碼塊時嵌削,所有該類的靜態(tài)方法或者被同一個類鎖修飾的同步代碼塊都會被鎖定毛好,其他線程無法訪問。
- 類鎖和對象鎖互不影響苛秕。
- 修飾方法和修飾代碼塊在本質(zhì)上是一樣的,區(qū)別只在于修飾代碼塊的方式在競爭激烈的情況下更加高效艇劫。
- 注意區(qū)別以下代碼的對象鎖的持有情況
public class Human {
//XiaoMing.eatDinner();
public synchronized void eatDinner() { //這里的鎖是XiaoMing
pickupByLeftHand();
eatFoodByMouth();
};
//XiaoMing.eatDinner2();
public void eatDinner() {
synchronized(this) { //這里的鎖是XiaoMing
pickupByLeftHand();
eatFoodByMouth();
}
};
//XiaoMing.drinkWater();
public void drinkWater() {
pickupByRightHand();
synchronized (mouth) { //這里的鎖是mouth
drinkWaterByMouth();
}
}
}
附:四種形式的鎖
-
對象鎖: 對單個實例對象的獨享內(nèi)存的部分區(qū)域加鎖
- 修飾非靜態(tài)方法
public synchronized void doSth() {}
- 修飾代碼塊
public void doSth() {
synchronized (obj) {
}
}
-
類鎖: 對整個類的共享內(nèi)存的部分區(qū)域加鎖
- 修飾靜態(tài)方法
public static synchronized void doSth() {}
- 修飾代碼塊
public void doSth() {
synchronized (ClassA.class) {
}
}