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