synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性
Java中每一個對象都可以作爲鎖,這是synchronized實現同步的基礎:
- 普通同步方法,鎖是當前實例對象;
- 靜態同步方法,鎖是當前類的class對象;
- 同步方法塊,鎖是括號裏面的對象。
1. synchronized對象鎖
synchronized可以修飾實例方法,主要有兩種:
public class MyObject {
synchronized public void methodA() {
//do something....
}
public class MyObject {
public void methodA() {
synchronized(this){
//do something....
}
}
synchronized 關鍵字鎖住的是當前對象。這也是稱爲對象鎖的原因。
2. 線程間通訊
傳統的線程通信可以藉助Object類提供的wait() notify() notifyAll()三個方法實現:
wait():導致當前線程等待,直到其他線程調用該同步監視器的notify()方法或notifyAll()方法來喚醒線程,調用wait()方法的當前線程會釋放對該同步監視器的鎖定;
notify():喚醒在此同步監視器上等待的單個線程,如果所有線程都在此同步監視器上等待,則會選擇喚醒其中一個線程(選擇是任意性的)。只有當前線程放棄對該同步監視器的鎖定後(使用wait()方法),纔可以執行被喚醒的線程;
notifyAll():喚醒在此同步監視器上等待的所有線程,只有當前線程放棄對該同步監視器的鎖定後(使用wait()方法),纔可以執行被喚醒的線程;
- 對於使用synchronized修飾的同步方法,因爲該類的默認實例(this)就是同步監視器,所以可以在同步方法中直接調用這三個方法;
- 對於使用synchronized修飾的同步代碼塊,同步監視器是synchronized後括號裏的對象,所以必須使用該對象調用者三個方法。
3. volatile
- volatile保證多線程共享變量可見性。
- 禁止指令重排序
- 不保證原子性
volatile 定義的變量,特殊性在於:
一個線程對 volatile 變量的寫一定對之後對這個變量的讀的線程可見。
即:
一個線程對 volatile 變量的讀一定能看見在它之前最後一個線程對這個變量的寫。
4. volatile 和 synchronized比較
當線程對 volatile變量寫時,java 會把值刷新到共享內存中;而對於synchronized,指的是當線程釋放鎖的時候,會將共享變量的值刷新到主內存中。
線程讀取volatile變量時,會將本地內存中的共享變量置爲無效;對於synchronized來說,當線程獲取鎖時,會將當前線程本地內存中的共享變量置爲無效。
synchronized 擴大了可見影響的範圍,擴大到了synchronized作用的代碼塊。