Java的全部對象都含有1個互斥鎖,這個鎖由JVM自動獲取和釋放。線程進入synchronized方法的時候獲取該對象的鎖,固然若是已經有線程獲取了這個對象的鎖,那麼當前線程會等待,synchronized方法正常返回或者拋異常而終止,JVM會自動釋放對象鎖。這裏也體現了用synchronized來加鎖的一個好處,方法拋異常的時候,鎖仍然能夠由JVM來自動釋放。
// 對象鎖:形式1(方法鎖) public synchronized void Method1() { System.out.println("我是對象鎖也是方法鎖"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } // 對象鎖:形式2(代碼塊形式) public void Method2() { synchronized (this) { System.out.println("我是對象鎖"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }
因爲一個class不論被實例化多少次,其中的靜態方法和靜態變量在內存中都只有一份。因此,一旦一個靜態的方法被申明爲synchronized。此類全部的實例化對象在調用此方法,共用同一把鎖,咱們稱之爲類鎖。
public synchronized static void Method3() { System.out.println("我是類鎖"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }
下面這段代碼是兩個類鎖和一個對象鎖,拿到鎖後,睡1秒鐘。
// 類鎖A public synchronized static void classLockA() { System.out.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"); } // 類鎖B public synchronized static void classLockB() { System.out.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"); } // 對象鎖 public synchronized void objectLock() { System.out.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"); }
建立三個線程類:分別調用一個資源中的三個方法
class ThreadA extends Thread { private Test02 test02; public ThreadA(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.classLockA(); } } class ThreadB extends Thread { private Test02 test02; public ThreadB(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.classLockB(); } } class ThreadC extends Thread { private Test02 test02; public ThreadC(Test02 tk) { test02 = tk; } // 調用對象鎖 public void run() { test02.objectLock(); } }
main方法:起了三個線程,共同訪問一個Test02對象
public static void main(String[] args){ Test02 test02 = new Test02(); ThreadA ta = new ThreadA(test02); ThreadB tb = new ThreadB(test02); ThreadC tc = new ThreadC(test02); ta.setName("A"); tb.setName("B"); tc.setName("C"); ta.start(); tb.start(); tc.start(); }
執行的結果:
name = A, begain name = C, begain name = A, end name = B, begain name = C, end name = B, end
能夠看出因爲 classLockA和classLockB都是類鎖,即同一個鎖,因此 A和B是按順序執行,即同步的。而C是對象鎖,和A/B不是同一種鎖,因此C和A、B是 異步執行的。
分析:java
對象鎖要想保持同步執行,那麼鎖住的必須是同一個對象,舉個例子:
Test02類不變,重起兩個線程類:均對對象鎖進行了調用多線程
class ThreadA extends Thread { private Test02 test02; public ThreadA(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.objectLock(); } } class ThreadB extends Thread { private Test02 test02; public ThreadB(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.objectLock(); } }
main方法:建立兩個不一樣的資源對象,啓動兩個線程,分別對加鎖的方法進行調用
public static void main(String[] args){ Test02 test02 = new Test02(); Test02 test03 = new Test02(); ThreadA ta = new ThreadA(test02); ThreadB tb = new ThreadB(test03); ta.setName("A"); tb.setName("B"); ta.start(); tb.start(); }
結果可見,是異步執行的,沒有達到同步的做用。
name = A, begain name = B, begain name = A, end name = B, end
改進:只需對類鎖進行調用,代碼以下:
class ThreadA extends Thread { private Test02 test02; public ThreadA(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.classLockA(); } } class ThreadB extends Thread { private Test02 test02; public ThreadB(Test02 tk) { test02 = tk; } // 調用類鎖 public void run() { test02.classLockA(); } }
main方法:一樣是建立了多個對象
public static void main(String[] args){ Test02 test02 = new Test02(); Test02 test03 = new Test02(); ThreadA ta = new ThreadA(test02); ThreadB tb = new ThreadB(test03); ta.setName("A"); tb.setName("B"); ta.start(); tb.start(); }
結果達到了同步的效果!
name = A, begain name = A, end name = B, begain name = B, end
總結:異步