在java中闺阱,如果開啟多個線程同時讀寫一個對象,會導(dǎo)致數(shù)據(jù)不正常舵变。為了保證數(shù)據(jù)的安全酣溃,我們會使用鎖來控制線程的訪問。
synchronized
synchronized纪隙,互斥鎖赊豌,當(dāng)一個線程持有鎖時,另一個線程無法持有绵咱,只有當(dāng)方法執(zhí)行完碘饼,鎖被釋放時,其他線程才有機(jī)會獲取鎖悲伶。
synchronized的使用有兩種方式:一種是用在方法上艾恼,一種是用在代碼塊上。
public synchronized void method ();
public void method () {
synchronized(obj){
}
}
當(dāng)其用在方法上時麸锉,線程調(diào)用該方法會獲取其實例的鎖钠绍,效果等同于在代碼塊上鎖住了自己的實例。
synchronized(this){
}
所以除非保證該方法的實例唯一花沉,或者為static修飾(static修飾時柳爽,對象鎖為改類的鎖)媳握,否則無法保證互斥。也就是說泻拦,只能互斥同一個對象鎖的線程毙芜。
wait
wait,使線程釋放當(dāng)前持有的對象鎖争拐,并進(jìn)入等待狀態(tài)腋粥,后續(xù)代碼不再執(zhí)行,其他線程可爭奪該對象鎖架曹。
notify
notify隘冲,喚醒其他因調(diào)用其對象鎖進(jìn)入wait狀態(tài)的線程。執(zhí)行完后續(xù)代碼之后釋放對象鎖绑雄。若有多個調(diào)用其對象進(jìn)入wait狀態(tài)的線程展辞,則根據(jù)系統(tǒng)的實現(xiàn),喚醒其中一個万牺。
notifyAll
notifyAll罗珍,喚醒所有其他因調(diào)用其對象鎖進(jìn)入wait狀態(tài)的線程。執(zhí)行完后續(xù)代碼之后釋放對象鎖脚粟。根據(jù)系統(tǒng)實現(xiàn)決定先喚醒哪一個覆旱。
注意
只有線程本身持有該對象鎖,才能執(zhí)行wait/notify/notifyAll方法核无,比如:
synchronized(obj){
obj.wait();
}
不然會報異常:IllegalMonitorStateException扣唱。
死鎖
有兩個線程,分別持有兩個對象鎖团南,并且互相申請對方的對象鎖時噪沙,會發(fā)生死鎖。
比如:
synchronized(a){
try{
Thread.sleep(1000);
}catch(Exception e){
}
synchronized(b){
}
}
synchronized(b){
try{
Thread.sleep(1000);
}catch(Exception e){
}
synchronized(a){
}
}
兩個線程分別持有對象鎖a吐根、b正歼,之后互相申請對方的對象鎖b、a佑惠,這個時候因為雙方都無法釋放自己的對象鎖朋腋,同時對方無法獲取對象鎖,就會進(jìn)入鎖死BLOCKED狀態(tài)膜楷。
線程可以持有多個對象鎖旭咽,并且當(dāng)其調(diào)用wait釋放其中一個對象鎖時,其扔持有其他對象鎖赌厅。比如:
synchronized(a){
synchronized(b){
b.wait();
}
}
當(dāng)線程調(diào)用b的對象鎖進(jìn)入等待狀態(tài)穷绵,并釋放了對象鎖b,這個時候依然保持著持有對象鎖a特愿,當(dāng)此線程被喚醒并釋放對象鎖a之前仲墨,其他線程依然無法獲取對象鎖a勾缭。