關於 Synchronized 的原理可以參見 聊聊併發(二)Java SE1.6中的Synchronized
概念
- 首先要強調的一點 Synchronized 並不是鎖它只是一個關鍵字,他的作用是標記方法或者代碼塊是同步的。Synchronized 關鍵字會直接鎖住公共內存,保證資源、數據的同步避免競爭。
- 本質上 Synchronized 鎖住的是一個叫 Monitor 的東西,如果一個 Monitor 被 Synchronized 標記,所有訪問此 Monitor 的動作都會被阻塞直到 Monitor 被釋放掉。
- 被 Synchronized 標記的代碼塊執行完畢後會自動釋放 Monitor 即解除鎖。
- Synchronzied 是一個很重的方法。
Synchronized 大概可以分爲以下幾種同步塊:
-
標記實例方法:實例同步方法
在方法聲明的時候加上 Synchronized 關鍵字,這將告訴 Java 此方法是同步的,此時 Synchronized 的 Monitor 是擁有該方法的對象。所以當前線程可以訪問此類的任何實例方法, 而其他線程需要等待 methodA() 結束。
這樣每一個 methodA() 方法都同步在不同的對象上,即該方法所屬的實例。如果有多個實例存在,那麼一個線程一次可以在一個實例同步塊中執行,即一個實例一個線程。
public class SynchronizedDemo {
/**
* 此時 synchronized 鎖住的是當前對象!
*/
public synchronized void methodA() {
……
}
}
-
標記靜態方法 :靜態同步方法
靜態方法同步和實例方法同步方法一樣,也使用synchronized 關鍵字。被 static 標記的方法也稱類方法,他是屬於類的而非具體對象,所以如果 Synchronized 方法標記了靜態方法,此時的 Monitor 將會是 SynchronizedDemo 的字節碼文件( SynchronizedDemo.class 也就是類對象),在 Java 虛擬機中一個類只有一個類對象,這意味着不管同一個類中的哪個靜態同步方法被訪問,其他的線程都需等待。即一個字節碼文件一個線程。
對於不同類的靜態同步方法,他們的 Monitoer 爲各自的字節碼文件,所以一個線程可以同時訪問不同類中的竟然方法而無需等待。
public class SynchronizedDemo {
/**
* staticA 的寫法等價於 staticB
*/
public static synchronized void staticA() {
}
public static void staticB() {
synchronized (SynchronizedDemo.class){
}
}
}
// 此方式將會鎖住當前實例
synchronized (this) {
}
// 鎖住字節碼文件,意味着 SynchronizedDemo 的所有實例都會被鎖住
synchronized (SynchronizedDemo.class) {
}
// 鎖住指定的實例,這種方式一般用於單獨判讀
synchronized (monitor) {
}
public class SynchronizedDemo {
/**
* staticA 、staticB 寫法等價
*/
public static synchronized void staticA() {
}
public static void staticB() {
synchronized (SynchronizedDemo.class){
}
}
}