一. this 鎖
同步函數其實用到的鎖就是 this 鎖,為什么他用到的是 this 鎖呢?為了證實這個結論我 們本節將會有兩個實驗性的程序來作為支撐,說服自己和讀者證明同步函數用到的就是 this 鎖好了,請看下第一個程序
需求:
我們定義一個類,其中有兩個方法,均加了同步鎖,假設函數的同步不是 this 鎖,我們 如果啟動一個線程調用方法 A,另外一個線程調用用方法 B,A 方法和 B 方法里均是死循環, 按照同步函數不是 this 鎖的邏輯,兩個函數中的邏輯將會被同時執行,但是情況是否是這樣, 我們需要通過代碼進行實驗
public class ClassA { public synchronized void A() { System.out.println("AAAAAAAAAAAAAAAAA"); while (true) { } } public synchronized void B() { System.out.println("BBBBBBBBBBBBBBBBB"); while (true) { } } }
public class MethodSynchronizedTest { public static void main(String[] args) { final ClassA clazz = new ClassA(); // 啟動一個線程 new Thread(new Runnable() { public void run() { clazz.A();// 調用A方法 } }).start(); // 啟動另一個線程 new Thread(new Runnable() { public void run() { clazz.B();// 調用B方法 } }).start(); } }
分別啟動了兩個線程,分別用來執行 ClassA 中的兩個方法 A 和 B,兩個方法都是加了鎖 的,也就是說某個線程盡到方法 A 中其他線程就不能進入 A,但是另一個線程應該能進入 B, 但是我們等了半天方法 B 仍然沒有輸出,因此我們得出一個結論,他們的鎖是同一個,至於 是哪一個鎖呢?答案就是 this 鎖;但是這個例子似乎還不過癮,那么我們繼續修正叫號的程 序,讓讀者有一個更加直觀的理解;
需求: 我們增加一個方法,也用來進行叫號,在 run 方法中也進行叫號,第一個線程調用 run 方法中的邏輯,第二個方法調用函數中的邏輯;
public class TicketWindow3 implements Runnable { private int max_value = 0; private Object lock = new Object(); private boolean flag = true; @Override public void run() { if (flag) { while (true) { //同步函數其實用到的鎖就是 this 鎖, this鎖針對的是對象 //靜態鎖,鎖是類的字節碼信息,因此如果一個類的函數為靜態方法,那么我們需要通過該類的 class 信息進行加鎖; synchronized (lock) { if (max_value > 500) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ":lock..." + max_value++); } } } else { while (true) if (ticket()) break; } } private synchronized boolean ticket() { if (max_value > 500) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ": method.." + max_value++); return false; } public void change() throws InterruptedException { Thread.sleep(30);// 讀者可以自行思考為什么要sleep this.flag = false; } }
public class Bank3 { public static void main(String[] args) throws InterruptedException { TicketWindow3 tw3 = new TicketWindow3(); Thread t1 = new Thread(tw3); Thread t2 = new Thread(tw3); t1.start(); tw3.change(); t2.start(); } }
文字說明: 可能到輸出性信息,其中會有 501 這樣的信息輸出,為什么會這樣呢?因為上述的代碼 兩處業務邏輯同步鎖是兩把鎖,如果您將 lock 換成 this,這個現象就不會出現,讀者可 以自己進行測試;
2 static 鎖
如果我們的方法 ticket 是一個靜態方法,再次測試一下您會發現,還是會出現 501 這 樣的輸出信息,根據之前的描述讀者可能會第一時間想到他們兩個用到的鎖不是同一把鎖, 因此我們將代碼在次做了修改
public class TicketWindow3 implements Runnable { private static int max_value = 0; private boolean flag = true; @Override public void run() { if (flag) { while (true) { //同步函數其實用到的鎖就是 this 鎖, this鎖針對的是對象 //靜態鎖,鎖是類的字節碼信息,因此如果一個類的函數為靜態方法,那么我們需要通過該類的 class 信息進行加鎖; synchronized (TicketWindow3.class) { if (max_value > 500) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ":lock..." + max_value++); } } } else { while (true) if (ticket()) break; } } private synchronized static boolean ticket() { if (max_value > 500) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ": method.." + max_value++); return false; } public void change() throws InterruptedException { Thread.sleep(30);// 讀者可以自行思考為什么要sleep this.flag = false; } }
靜態鎖,鎖是類的字節碼信息,因此如果一個類的函數為靜態方法,那么我們需要通過 該類的 class 信息進行加鎖;