Java 多執行緒全域性鎖與物件鎖
阿新 • • 發佈:2019-02-01
我們看一個例子:
class Demo {
public synchronized void test() {
System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("test方法執行完畢,當前執行緒為:" +Thread.currentThread().getName());
}
}
class MyThread implements Runnable {
@Override
public void run() {
Demo demo = new Demo();
demo.test();
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread,"子執行緒A").start();
new Thread(myThread,"子執行緒B").start();
new Thread(myThread,"子執行緒C").start();
}
}
執行結果:
從執行結果我們可以看出,Demo類提供的test同步方法好像並沒有起作用,這是怎麼一回事。
實際上,synchronized(this) 以及非 static 的 synchronized 方法,只能防止多個執行緒同時執行同一個物件的同步程式碼塊。即 synchronized 鎖住的是括號裡的物件,而不是程式碼塊
所以說 synchronized 是一個物件鎖。
當 synchronized 鎖住一個物件後,別的執行緒如果也想拿到這個物件的鎖,就必須等待這個執行緒執行完成釋放鎖,才能再次給物件加鎖,這樣才能達到執行緒同步的目的。所以即使兩個不同的程式碼塊都要鎖住同一個物件,那麼這兩個程式碼段也不能在多執行緒環境下同時執行,必須等其中一個現將物件鎖釋放掉,另一個才能給物件上鎖。
所以在上例中,MyThread執行緒類啟動三次也建立了三個Demo類,並且對其呼叫,三個不同的物件進入了同步方法中,所以顯示如上結果。
當一個執行緒A 進入到同步方法所在的類中,其他執行緒不能進入該類中的其他類中,因為鎖住的是物件。類比:廁所裡有個電視機,某人上廁所時關上了鎖,其他人也不能進來看電視。
那我們如果想將一段程式碼鎖住,使同時有且只有一個物件能訪問該程式碼塊應該如何操作。
這種鎖住程式碼塊的的操作叫做全域性鎖,可以通過以下兩種途徑實現:
1.1 鎖住同一個物件
class Demo {
public void test() {
// 鎖住進入的方法的物件
synchronized(this) {
System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("test方法執行完畢,當前執行緒為:"+Thread.currentThread().getName());
}
}
}
class MyThread implements Runnable {
// 為了防止多個執行緒建立多個物件,所以在類中自己建立一個物件
private Demo demo;
// 在構造方MyThread時將真正的物件傳入
public MyThread(Demo demo) {
this.demo = demo;
}
@Override
public void run() {
this.demo.test();
}
}
public class Test {
public static void main(String[] args) {
// 實際上,整個程式只有這一個物件
// 鎖住了該物件就相當於將 Demo類中的test方法程式碼鎖住了,曲線救國實現全域性鎖
Demo demo = new Demo();
MyThread myThread = new MyThread(demo);
new Thread(myThread,"子執行緒A").start();
new Thread(myThread,"子執行緒B").start();
new Thread(myThread,"子執行緒C").start();
}
}
1.2 鎖住整個類
class Demo {
public void test() {
// 將 Demo類 作為鎖定的物件,每次只能有一個物件進入該類
synchronized(Demo.class) {
System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("test方法執行完畢,當前執行緒為:"+Thread.currentThread().getName());
}
}
}
class MyThread implements Runnable {
@Override
public void run() {
// 雖然這裡還是存在建立多個物件的問題
// 但是由於test方法這次鎖住了整個類,所以同時有且僅有一個物件能夠進入Demo類中
Demo demo = new Demo();
demo.test();
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread,"子執行緒A").start();
new Thread(myThread,"子執行緒B").start();
new Thread(myThread,"子執行緒C").start();
}
}
執行結果:
當然,使用靜態同步方法也可以實現鎖住整個類的效果。
public static synchronized test() {
// statement
}