可重入鎖指同一個線程可以再次獲得之前已經獲得的鎖,避免產生死鎖。
Java中的可重入鎖:synchronized 和 java.util.concurrent.locks.ReentrantLock。
1、synchronized 使用方便,編譯器來加鎖,是非公平鎖。
2、ReenTrantLock 使用靈活,鎖的公平性可以定制。
3、相同加鎖場景下,推薦使用 synchronized。
ReenTrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的線程先獲得鎖。
鎖的實現:
Synchronized是依賴於JVM實現的,而ReenTrantLock是JDK實現的,有什么區別,說白了就類似於操作系統來控制實現和用戶自己敲代碼實現的區別。前者的實現是比較難見到的,后者有直接的源碼可供閱讀。
ReenTrantLock實現的原理:
簡單來說,ReenTrantLock的實現是一種自旋鎖,通過循環調用CAS操作來實現加鎖。它的性能比較好也是因為避免了使線程進入內核態的阻塞狀態。想盡辦法避免線程進入內核的阻塞狀態是我們去分析和理解鎖設計的關鍵鑰匙。
模擬ReenTrantLock用法:假設有兩個線程(A線程、B線程)去調用print(String name)方法,A線程負責打印'zhangsan'字符串,B線程負責打印'lisi'字符串。
a、當沒有為print(String name)方法加上鎖時,則會產生A線程還沒有執行完畢,B線程已開始執行,那么打印出來的name就會出現如下問題。
b、當為print(String name)方法加上鎖時,則會產生A執行完畢后,B線程才執行print(String name)方法,達到互斥或者說同步效果。
package com.lynch.lock; /** * ReentrantLock用法 * * @author Lynch */ public class ReentrantLockTest { public static void main(String[] args) { final Outputer outputer = new Outputer(); //線程A打印zhangsan new Thread(() -> { while (true) { sleep(); outputer.output("zhangsan"); } }).start(); //線程B打印lisi new Thread(() -> { while (true) { sleep(); outputer.output("lisi"); } }).start(); } private static void sleep() { try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.lynch.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 打印字符 * * @author Lynch */ public class Outputer { Lock lock = new ReentrantLock(); public void output(String name) { try { lock.lock(); int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } finally { lock.unlock(); } } }