我們看一個例子:
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
}
————————————————
原文鏈接:https://blog.csdn.net/weixin_40739833/article/details/80293480