從一個問題說起
一直想要把一些學習歷程和感悟記錄下來,卻沒有靜下心來好好思考一下,今天,就從同學問到的一個問題開始,對同一個類的靜態方法加鎖后,調用該方法,其他方法的調用會受到影響嗎?
對象鎖與類鎖
JAVA中每一個對象都持有一把鎖–monitor,monitor在操作系統中被稱為管程,也可翻譯為監視器,java中的monitor其實是對操作系統中monitor的一種實現(類似于接口與實現)。
對象鎖和類鎖本質其實是一樣的,只不過對象鎖指的是實例對象(所持有的monitor),而類鎖指的是類的Class對象。
synchronized
java中提供synchronized關鍵字與Object等來實現monitor機制的粒度控制。
synchronized修飾靜態方法,此刻的鎖指的是實例對象;
public synchronized static void syncStaticMethod(){
System.out.println("我是靜態方法,我被synchronized修飾。");
}
synchronized修飾非靜態方法,此刻的鎖指的是類的Class對象;
public synchronized void syncMethod(){
System.out.println("我是非靜態方法,我被synchronized修飾。");
}
synchronized修飾代碼塊,此刻的鎖指的是你所賦予synchronized的對象,與方法是否靜態無關;
public static void syncStaticBlockMethod() {
synchronized (Object.class) {
System.out.println("我是靜態方法,我被synchronized代碼塊修飾,此時的鎖對象是Object的Class對象。");
}
}
public void syncBlockMethod(){
synchronized (Object.class){
System.out.println("我是非靜態方法,我被synchronized代碼塊修飾,此時的鎖對象是Object的Class對象。");
}
}
分析
那么,對一個類的靜態方法加鎖,意味著什么?
其實,從上面我們已經可以得到答案了:當這個類的靜態方法被調用時,它會去獲取類鎖,準確的說是該類的Class對象的monitor,那么,其他方法會受到影響嗎?在這里,不妨做一個假設,那就是只要其他方法會競爭類的Class對象鎖,那么它便會陷入阻塞狀態(BLOCKED),直到獲取Class對象鎖,否則,便沒有影響。
當然,最初,我們還是應當使用代碼來證明(復制粘貼即可運行),如下:
/**
* @program: thinking-in-all
* @description:
* @author: Lucifinil
* @create: 2019-12-11
**/
public class SyncStaticYesAndNo implements Runnable {
public synchronized static void syncStaticYes1() {
try {
System.out.println(Thread.currentThread().getName() + " : 加鎖的靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 加鎖的靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void syncStaticYes2() {
try {
System.out.println(Thread.currentThread().getName() + " : 加鎖的靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 加鎖的靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void syncStaticNo() {
try {
System.out.println(Thread.currentThread().getName() + " : 不加鎖的靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 不加鎖的靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void syncYes() {
try {
System.out.println(Thread.currentThread().getName() + " : 加鎖的非靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 加鎖的非靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void syncNo() {
try {
System.out.println(Thread.currentThread().getName() + " : 不加鎖的非靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 不加鎖的非靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void syncStaticClassYes() {
synchronized (SyncStaticYesAndNo.class) {
try {
System.out.println(Thread.currentThread().getName() + " : 加Class對象鎖的靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 加Class對象鎖的靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void syncClassYes() {
synchronized (SyncStaticYesAndNo.class) {
try {
System.out.println(Thread.currentThread().getName() + " : 加Class對象鎖的非靜態方法運行開始n");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " : 加Class對象鎖的非靜態方法運行結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
syncStaticYes1();
} else if (Thread.currentThread().getName().equals("Thread-1")) {
syncStaticYes2();
} else if (Thread.currentThread().getName().equals("Thread-2")) {
syncStaticNo();
} else if (Thread.currentThread().getName().equals("Thread-3")) {
syncYes();
} else if (Thread.currentThread().getName().equals("Thread-4")) {
syncNo();
} else if (Thread.currentThread().getName().equals("Thread-5")) {
syncStaticClassYes();
} else if (Thread.currentThread().getName().equals("Thread-6")) {
syncClassYes();
}
}
public static void main(String[] args) {
SyncStaticYesAndNo obj = new SyncStaticYesAndNo();
Thread t1 = new Thread(obj);
Thread t2 = new Thread(obj);
Thread t3 = new Thread(obj);
Thread t4 = new Thread(obj);
Thread t5 = new Thread(obj);
Thread t6= new Thread(obj);
Thread t7 = new Thread(obj);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
}
}
這里,我們使用了七個線程來模擬多個情況,然后幾乎同時調用七個方法,由之前的分析,我們可以推測,只有競爭統一把鎖,才會產生阻塞情況,所以結果如下:
Thread-1 : 加鎖的靜態方法運行開始
Thread-3 : 加鎖的非靜態方法運行開始
Thread-2 : 不加鎖的靜態方法運行開始
Thread-4 : 不加鎖的非靜態方法運行開始
Thread-3 : 加鎖的非靜態方法運行結束
Thread-1 : 加鎖的靜態方法運行結束
Thread-2 : 不加鎖的靜態方法運行結束
Thread-6 : 加Class對象鎖的非靜態方法運行開始
Thread-4 : 不加鎖的非靜態方法運行結束
Thread-6 : 加Class對象鎖的非靜態方法運行結束
Thread-5 : 加Class對象鎖的靜態方法運行開始
Thread-5 : 加Class對象鎖的靜態方法運行結束
Thread-0 : 加鎖的靜態方法運行開始
Thread-0 : 加鎖的靜態方法運行結束
結論
我們可以看到,不加鎖的沒有受到任何影響,而加了鎖的非靜態方法也沒有受到任何影響,因為它所競爭的鎖并非是Class對象鎖,而是實例對象鎖,受到影響的有synchronized修飾的靜態方法,還有便是加了Class對象鎖的方法,本質上便是它們都在競爭當前類的Class對象鎖。