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