好處:解決了線程安全問題。
弊端:相對降低性能,因?yàn)榕袛噫i需要消耗資源,產(chǎn)生了死鎖。
定義同步是有前提的:
1,必須要有兩個(gè)或者兩個(gè)以上的線程,才需要同步。
2,多個(gè)線程必須保證使用的是同一個(gè)鎖。
java培訓(xùn)項(xiàng)目實(shí)戰(zhàn)中,同步的第二種表現(xiàn)形式:
同步函數(shù):其實(shí)就是將同步關(guān)鍵字定義在函數(shù)上,讓函數(shù)具備了同步性。
同步函數(shù)是用的哪個(gè)鎖呢?
通過驗(yàn)證,函數(shù)都有自己所屬的對象this,所以同步函數(shù)所使用的鎖就是this鎖。
當(dāng)同步函數(shù)被static修飾時(shí),這時(shí)的同步用的是哪個(gè)鎖呢?
靜態(tài)函數(shù)在加載時(shí)所屬于類,這時(shí)有可能還沒有該類產(chǎn)生的對象,但是該類的字節(jié)碼文件加載進(jìn)內(nèi)存就已經(jīng)被封裝成了對象,這個(gè)對象就是該類的字節(jié)碼文件對象。
所以靜態(tài)加載時(shí),只有一個(gè)對象存在,那么靜態(tài)同步函數(shù)就使用的這個(gè)對象。
這個(gè)對象就是 類名.class
在java培訓(xùn)項(xiàng)目實(shí)戰(zhàn)中
同步代碼塊和同步函數(shù)的區(qū)別?
同步代碼塊使用的鎖可以是任意對象。
同步函數(shù)使用的鎖是this,靜態(tài)同步函數(shù)的鎖是該類的字節(jié)碼文件對象。
在一個(gè)類中只有一個(gè)同步,可以使用同步函數(shù)。如果有多同步,必須使用同步代碼塊,來確定不同的鎖。所以同步代碼塊相對靈活一些。
-------------------------------------------------------
★考點(diǎn)問題:請寫一個(gè)延遲加載的單例模式?寫懶漢式;當(dāng)出現(xiàn)多線程訪問時(shí)怎么解決?加同步,解決安全問題;效率高嗎?不高;怎樣解決?通過雙重判斷的形式解決。
//懶漢式:延遲加載方式。
當(dāng)多線程訪問懶漢式時(shí),因?yàn)閼袧h式的方法內(nèi)對共性數(shù)據(jù)進(jìn)行多條語句的操作。所以容易出現(xiàn)線程安全問題。為了解決,加入同步機(jī)制,解決安全問題。但是卻帶來了效率降低。
為了效率問題,通過雙重判斷的形式解決。
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //鎖是誰?字節(jié)碼文件對象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
---------------------------------------------------------
同步死鎖:通常只要將同步進(jìn)行嵌套,就可以看到現(xiàn)象。同步函數(shù)中有同步代碼塊,同步代碼塊中還有同步函數(shù)。
java培訓(xùn)實(shí)戰(zhàn)項(xiàng)目中
線程間通信:思路:多個(gè)線程在操作同一個(gè)資源,但是操作的動作卻不一樣。
1:將資源封裝成對象。
2:將線程執(zhí)行的任務(wù)(任務(wù)其實(shí)就是run方法。)也封裝成對象。
等待喚醒機(jī)制:涉及的方法:
wait:將同步中的線程處于凍結(jié)狀態(tài)。釋放了執(zhí)行權(quán),釋放了資格。同時(shí)將線程對象存儲到線程池中。
notify:喚醒線程池中某一個(gè)等待線程。
notifyAll:喚醒的是線程池中的所有線程。
注意:
1:這些方法都需要定義在同步中。
2:因?yàn)檫@些方法必須要標(biāo)示所屬的鎖。
你要知道 A鎖上的線程被wait了,那這個(gè)線程就相當(dāng)于處于A鎖的線程池中,只能A鎖的notify喚醒。
3:這三個(gè)方法都定義在Object類中。為什么操作線程的方法定義在Object類中?
因?yàn)檫@三個(gè)方法都需要定義同步內(nèi),并標(biāo)示所屬的同步鎖,既然被鎖調(diào)用,而鎖又可以是任意對象,那么能被任意對象調(diào)用的方法一定定義在Object類中。
wait和sleep區(qū)別: 分析這兩個(gè)方法:從執(zhí)行權(quán)和鎖上來分析:
wait:可以指定時(shí)間也可以不指定時(shí)間。不指定時(shí)間,只能由對應(yīng)的notify或者notifyAll來喚醒。
sleep:必須指定時(shí)間,時(shí)間到自動從凍結(jié)狀態(tài)轉(zhuǎn)成運(yùn)行狀態(tài)(臨時(shí)阻塞狀態(tài))。
wait:線程會釋放執(zhí)行權(quán),而且線程會釋放鎖。
Sleep:線程會釋放執(zhí)行權(quán),但不是不釋放鎖。
線程的停止:通過stop方法就可以停止線程。但是這個(gè)方式過時(shí)了。
停止線程:原理就是:讓線程運(yùn)行的代碼結(jié)束,也就是結(jié)束run方法。
怎么結(jié)束run方法?一般run方法里肯定定義循環(huán)。所以只要結(jié)束循環(huán)即可。
第一種方式:定義循環(huán)的結(jié)束標(biāo)記。
第二種方式:如果線程處于了凍結(jié)狀態(tài),是不可能讀到標(biāo)記的,這時(shí)就需要通過Thread類中的interrupt方法,將其凍結(jié)狀態(tài)強(qiáng)制清除。讓線程恢復(fù)具備執(zhí)行資格的狀態(tài),讓線程可以讀到標(biāo)記,并結(jié)束。
---------< java.lang.Thread >----------
interrupt():中斷線程。
setPriority(int newPriority):更改線程的優(yōu)先級。
getPriority():返回線程的優(yōu)先級。
toString():返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級和線程組。
Thread.yield():暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
setDaemon(true):將該線程標(biāo)記為守護(hù)線程或用戶線程。將該線程標(biāo)記為守護(hù)線程或用戶線程。當(dāng)正在運(yùn)行的線程都是守護(hù)線程時(shí),Java 虛擬機(jī)退出。該方法必須在啟動線程前調(diào)用。
join:臨時(shí)加入一個(gè)線程的時(shí)候可以使用join方法。
當(dāng)A線程執(zhí)行到了B線程的join方式。A線程處于凍結(jié)狀態(tài),釋放了執(zhí)行權(quán),B開始執(zhí)行。A什么時(shí)候執(zhí)行呢?只有當(dāng)B線程運(yùn)行結(jié)束后,A才從凍結(jié)狀態(tài)恢復(fù)運(yùn)行狀態(tài)執(zhí)行。
本文版權(quán)歸黑馬程序員Java培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!
作者:黑馬程序員Java培訓(xùn)學(xué)院
首發(fā):http://java.itheima.com/