方法鎖形式: synchronized 修飾普通方法, 鎖對象默認爲 this ,也就是當前實例對象html
注意若是是 不一樣 類鎖的 不一樣 的同步代碼塊。狀況是有點特殊的,雖然也是有順序了。java
package com.my.com.my.sysnc; /** * 同步鎖測試 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { synchronized (lock1){ System.out.println("lock1對象鎖代碼塊,線程名"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束lock1"); } synchronized (lock2){ System.out.println("lock2對象鎖代碼塊,線程名"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束lock2"); } } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 結果 即 ,在run 方法裏面, 當 第一個線程執行完 第一個同步代碼塊的時候,在開始執行第一個代碼塊的時候。 此時 第二個線程會去執行第一個同步代碼塊。 這樣就有 點並行執行了 : lock1對象鎖代碼塊,線程名Thread-0 Thread-0結束lock1 lock2對象鎖代碼塊,線程名Thread-0 lock1對象鎖代碼塊,線程名Thread-1 Thread-0結束lock2 Thread-1結束lock1 lock2對象鎖代碼塊,線程名Thread-1 Thread-1結束lock2
類鎖面試
若是是對象鎖的話,那麼不一樣的對象對於不一樣的線程來講,就不會起到 鎖的做用了。ide
若是是 類鎖的話,那麼就能夠作到 不一樣的對象對於不一樣的線程 起到 鎖的做用。函數
普通方法: 即非 靜態方法測試
拋出異常的適合, LOCK 鎖不會釋放鎖,而 synchronized 會釋放鎖this
面試題spa
1. 兩個線程同時訪問一個對象的同步方法:.net
可以鎖住,同一個實例線程
2. 兩個線程訪問的是兩個對象的同步方法。
由於不一樣的實例全部並無鎖的做用,鎖對象不是同一個
3. 兩個線程訪問的是 synchronized 的靜態方法
鎖的是 class對象, 鎖住
4. 同時訪問同步方法與非同步方法
同步方法不能影響 非同步方法。
同步方法與非同步方法能夠同時運行
5. 訪問同一個對象的不一樣的普通同步方法
方法不能同時的運行。 鎖的是同一個對象。受鎖的影響
例子:
package com.my.com.my.sysnc; /** * 同步鎖測試 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public synchronized void test1(){ System.out.println("對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束線程"); } public synchronized void test2(){ System.out.println("對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束線程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 鎖的是同一個實例,競爭的是同一個實例,因此受到影響 可能的結果 : 對象鎖代碼塊Thread-1 Thread-1結束線程 對象鎖代碼塊Thread-0 Thread-0結束線程
6. 同時訪問靜態 synchronized 和 非靜態 synchronized 方法
鎖不其做用,兩個方法均可以一塊兒運行。
由於靜態鎖的是 class 對象,非靜態的 鎖的是 實例對象。是不同的
package com.my.com.my.sysnc; /** * 同步鎖測試 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public static synchronized void test1(){ System.out.println("靜態加鎖方法1,,對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束線程"); } public synchronized void test2(){ System.out.println("非靜態加鎖方法2,對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束線程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 結果: 非靜態加鎖方法2,對象鎖代碼塊Thread-1 靜態加鎖方法1,,對象鎖代碼塊Thread-0 Thread-0結束線程 Thread-1結束線程
7. 方法拋異常後,會釋放鎖
package com.my.com.my.sysnc; /** * 同步鎖測試 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public synchronized void test1(){ System.out.println("對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } // 拋出異常後,釋放鎖,由JVM處理釋放鎖 throw new RuntimeException("拋出異常"); } public synchronized void test2(){ System.out.println("對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結束線程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 結果: 對象鎖代碼塊Thread-0 對象鎖代碼塊Thread-1 Exception in thread "Thread-0" java.lang.RuntimeException: 拋出異常 at com.my.com.my.sysnc.TestSynchronized.test1(TestSynchronized.java:36) at com.my.com.my.sysnc.TestSynchronized.run(TestSynchronized.java:17) at java.lang.Thread.run(Thread.java:748) Thread-1結束線程
性質
1, 可重入:避免死鎖,提高封裝性
指的是 同一線程的外層函數得到鎖以後,內層函數能夠之間再次獲取該鎖
同一個方法可重入(好比遞歸調用),
不要求同一個方法也能夠重入(好比同一個類中調用2個都是鎖的方法) ,
可重入不要求 是同一個類中的(子類的 重寫父類的加鎖方法,並調用父類的加鎖方法)
2,不可中斷
3. 粒度
線程而非調用
原理
由於每個對象均可以做爲 鎖的部分。
因此每一個對象都有一個 內置鎖。
線程在進入 synchronized 裏面以前,會獲取 synchronized 鎖的對象的 內置鎖。
當 執行完 synchronized 的 鎖的代碼後,會釋放 對應的對象的 內置鎖。
Java 對象的對象頭有一個 字段用來講明是否鎖住了,因此 synchronized 做用就是 根據這個字段來 鎖的
編譯執行的時候 ,JVM 經過 Java的 指令 monitorenter 進入 鎖(monitor計數器加1), monitorexit 退出 鎖(monitor計數器減1)。
經過判斷 monitor 數字 判斷是否 是否能夠獲取鎖 。
注意雖然synchronized 鎖 等價於 Lock 鎖。 可是他們其實仍是有所區別的。
lock 鎖並非鎖當前的 this 對象的。
package com.my.com.my.sysnc; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 同步鎖測試 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Lock lock=new ReentrantLock(); @Override public void run() { test1(); test2(); } public synchronized void test1(){ System.out.println("synchronized對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synchronized對象鎖代碼塊結束"+Thread.currentThread().getName()); } public void test2(){ lock.lock(); try { System.out.println("lock對象鎖代碼塊"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } }finally { //不釋放鎖 // lock.unlock(); } System.out.println(Thread.currentThread().getName()+"結束線程lock"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 線程0 在lock 執行後,並無釋放lock 鎖。 因此線程1 會在 lock 處等待。 由於 lock 不是 鎖當前this 對象, 因此 線程1 能夠執行 synchronized 鎖方法 結果: synchronized對象鎖代碼塊Thread-0 synchronized對象鎖代碼塊結束Thread-0 lock對象鎖代碼塊Thread-0 synchronized對象鎖代碼塊Thread-1 Thread-0結束線程lock synchronized對象鎖代碼塊結束Thread-1
注意點
1, 鎖對象不能爲空,做用域不宜過大,避免死鎖
2, 如何選擇 Lock 和 synchronized 關鍵字
21, 能使用 synchronized 優先使用,可以使代碼更簡化
22. 要使用 LOCK 的獨有特性才考慮 lock
23. 總之就是 避免出錯,追求穩定的
synchronized非公平鎖,ReentrantLock能夠設置是否公平鎖
參考 https://www.cnblogs.com/twoheads/p/10150063.html
鎖 | 優勢 | 缺點 | 適用場景 |
---|---|---|---|
偏向鎖 | 加鎖和解鎖不須要額外的消耗,與執行非同步方法僅存在納秒級的差距 | 若是線程間存在競爭,會帶來額外的鎖撤銷的消耗 | 適用於只有一個線程訪問同步塊的狀況 |
輕量級鎖 | 競爭的線程不會堵塞,提升了程序的響應速度 | 始終得不到鎖的線程,使用自旋會消耗CPU | 追求響應時間,同步塊執行速度很是塊,只有兩個線程競爭鎖 |
重量級鎖 | 線程競爭不使用自旋,不會消耗CPU | 線程堵塞,響應時間緩慢 | 追求吞吐量,同步塊執行速度比較慢,競爭鎖的線程大於2個 |
以上來自慕課網