作者:小牛呼嚕嚕 | https://xiaoniuhululu.com
計算機內功、JAVA底層、面試、職業成長相關資料等更多精彩文章在公眾號「小牛呼嚕嚕」
大家好,我是呼嚕嚕,在之前的文章中https://mp.weixin.qq.com/s/0Ii636KQ9sWwX-OhdlPIYw,我們知道了volatile關鍵字
可以保證可見性、有序性,但無法保證原子性的。今天我們來聊聊synchronized關鍵字,其可以同時保證三者,實現執行緒安全。
在介紹synchronized關鍵字
之前,我們得強調一下什麼是執行緒安全,所謂執行緒安全:
當多個執行緒同時存取一個物件時, 如果不用考慮這些執行緒在執行時環境下的排程和交替執行, 也不需要進行額外的同步, 或者在呼叫方進行任何其他的協調操作, 呼叫這個物件的行為都可以獲得正確的結果, 那就稱這個物件是執行緒安全的。
在 Java 早期版本中,synchronized 屬於 重量級鎖,效率低下;不過在 Java 6 之後,Java 官方對從 JVM 層面對 synchronized 較大優化,所以現在的 synchronized 鎖效率也優化得非常不錯。目前不論是各種開源框架還是 JDK 原始碼都大量使用了 synchronized 關鍵字
synchronized
的使用其實比較簡單,可以用它來修飾實體方法和靜態方法,也可以用來修飾程式碼塊。我們需要注意的是synchronized
是一個物件鎖,也就是它鎖的是一個物件。我們無論使用哪一種方法,synchronized
都需要有一個鎖物件
synchronized修飾實體方法, 在方法上加上synchronized關鍵字即可。
public class SynchronizedTest1 {
public synchronized void test() {
System.out.println("synchronized 修飾 方法");
}
}
此時,synchronized加鎖的物件就是這個方法所在範例的本身,作用於當前範例加鎖,進入同步程式碼前要獲得**當前範例的鎖 **。
補充一個常見的面試題:構造方法可以用synchronized關鍵字修飾嗎?
不能,也不需要,因為構造方法本身就是執行緒安全的
synchronized修飾靜態方法的使用與實體方法並無差別,在靜態方法上加上synchronized關鍵字即可
public static synchronized void test(){
i++;
}
由於靜態方法不屬於任何一個範例物件,歸整個類所有,不依賴於類的特定範例,被類的所有範例共用。給靜態方法加synchronized
鎖,會作用於類的所有物件範例 ,進入同步程式碼前要獲得 當前靜態方法所在類的Class物件的鎖。
有一點我們需要知道:如果一個執行緒 A 呼叫一個範例物件的非靜態 synchronized 方法,而執行緒 B 需要呼叫這個範例物件所屬類的靜態 synchronized 方法,是允許的,不會發生互斥現象,因為存取靜態 synchronized 方法佔用的鎖是當前類的鎖,而存取非靜態 synchronized 方法佔用的鎖是當前範例物件鎖。
synchronized修飾程式碼塊需要傳入一個物件。
public class SynchronizedTest2 {
public void test() {
synchronized (this) {
System.out.println("synchronized 修飾 程式碼塊");
}
}
}
此時synchronized加鎖物件即為傳入的這個物件範例,指定加鎖物件,進入同步程式碼庫前要獲得給定物件的鎖
需要注意的是這裡的this:
- synchronized(object) ,表示進入同步程式碼庫前要獲得 給定物件的鎖
- synchronized(類.class) ,表示進入同步程式碼前要獲得 給定 Class 的鎖
- 最好不要使用 synchronized(String a) ,因為在 JVM 中,字串常數池具有快取功能,如果我們多次加鎖,會加鎖在同一個物件上