傻瓜源碼-內容簡介 |
---|
🤪【職場經驗】(持續更新) 精編短文:如何成爲值錢的Java開發-指南 如何平常學習、如何書寫簡歷、引導面試官、系統準備面試、選擇offer、提升績效、晉升TeamLeader..... |
🧐【源碼解讀】(持續更新) <br/>1. 源碼選材:Java架構師必須掌握的全部框架和類庫源碼<br/>2. 內容大綱:按照「企業應用Demo」講解執行源碼:總綱「閱讀指南」、第一章「源碼基礎」、第二章「相關Java基礎」、第三章「白話講源碼」、第四章「代碼解讀」、第五章「設計模式」、第六章「附錄-面試習題、相關JDK方法、中文註釋可運行源碼項目」 3. 讀後問題:粉絲羣答疑解惑 |
已收錄:HashMap、ReentrantLock、ThreadPoolExecutor、《Spring源碼解讀》、《Dubbo源碼解讀》..... |
🤩【面試題集】(持續更新)<br/>1. 面試題選材:Java面試常問的全部面試題和必會知識點<br/>2. 內容大綱:第一部分」注意事項「、第二部分「面試題解讀」(包括:」面試題「、」答案「、」答案詳解「、「實際開發解說」) 3. 深度/廣度:面試題集中的答案和答案詳解,都是對齊通常面試要求的深度和廣度 4. 讀後問題:粉絲羣答疑解惑 |
已收錄:Java基礎面試題集、Java併發面試題集、JVM面試題集、數據庫(Mysql)面試題集、緩存(Redis)面試題集 ..... |
🤤【粉絲羣】(持續更新) <br/>收錄:阿里、字節跳動、京東、小米、美團、嗶哩嗶哩等大廠內推 |
😛 做者介紹:Spring系源碼貢獻者、世界五百強互聯網公司、TeamLeader、Github開源產品做者 😛 做者微信:wowangle03 (企業內推聯繫我) |
加入個人粉絲社羣,閱讀更多內容。從學習到面試,從面試到工做,從 coder 到 TeamLeader,天天給你答疑解惑,還能有第二份收入!html
本文建議分爲兩個學習階段,掌握了第一階段,再進行第二階段;java
源碼項目中的註釋含義:node
ReentrantLock 是 java.util.concurrent 併發包中的可重入鎖(可重入鎖就是指持有鎖的線程能夠重複進入有該鎖的代碼塊);基於 AQS(AbstractQueuedSynchronized)實現。須要手動釋放鎖,可是支持更多方法,好比:上公平/非公平鎖、可被中斷鎖、可被中判定時鎖等。面試
@Test public void testReentrantLock1() { // 多個線程使用同一個 ReentrantLock 對象,上同一把鎖,默認上非公平鎖 Lock lockNoFair = new ReentrantLock(); try { // 本線程嘗試獲取鎖;若是鎖已經被其它線程持有,則會進入阻塞狀態,直到獲取到鎖 lockNoFair.lock(); System.out.println("處理中..."); } finally { // 釋放鎖 lockNoFair.unlock(); } } @Test public void testReentrantLock2() { // 多個線程使用同一個 ReentrantLock 對象,上同一把鎖,構造函數傳 true,上公平鎖 Lock lockFair = new ReentrantLock(true); try { // 本線程嘗試獲取鎖;若是鎖已經被其它線程持有,則會進入阻塞狀態,直到獲取到鎖 lockFair.lock(); System.out.println("處理中..."); } finally { // 釋放鎖 lockFair.unlock(); } }
1. lock()(公平鎖)sql
線程獲取鎖的順序徹底基於調用 lock() 方法的前後順序。數據庫
時間線 | 線程 1 | 線程 2 | 線程 3 |
---|---|---|---|
1 | 線程 1 調用 lockFair.lock() 方法,獲取到鎖 | ||
2 | 線程 2 調用 lockFair.lock() 方法後,沒有獲取到鎖,將線程 2 順序放入到鏈表裏排隊,進入阻塞狀態 | ||
3 | 線程 1 調用 lockFair.unLock() 方法,釋放鎖,喚醒線程 2 | ||
4 | 線程 3 調用 lockFair.lock() 方法,發現線程 2 已經在鏈表裏等待得到鎖;線程 3 就追加到線程 2 以後,進行排隊 | ||
5 | 線程 2 被線程 1 喚醒,從新嘗試獲取鎖,獲取鎖成功 |
2. lock()(非公平鎖)設計模式
時間線 | 線程 1 | 線程 2 | 線程 3 |
---|---|---|---|
1 | 調用 lockNoFair.lock() 方法,獲取到鎖 | ||
2 | 調用 lockNoFair.lock() 方法後,沒有獲取到鎖,線程 2 順序追加到鏈表後排隊,進入阻塞狀態 | ||
3 | 調用 lockNoFair.unLock() 方法,釋放鎖,喚醒線程 2 | ||
4 | 調用 lockNoFair.lock() 方法,成功獲取到鎖 | ||
5 | 線程 2 被線程 1 喚醒,呆在鏈表裏位置不動,從新嘗試獲取鎖,獲取鎖失敗,已經被線程 3 搶佔到了鎖,再次進入阻塞狀態 |
3. lockInterruptibly()(可被中斷鎖)緩存
lockInterruptibly() 也分爲公平鎖和非公平鎖,與 lock() 方法的區別就在於:當線程調用 lockInterruptibly() 方法沒有獲取到鎖,進入阻塞後;若是其它線程對該線程標記爲中斷狀態, lockInterruptibly() 方法則會從阻塞中喚醒,拋出中斷異常。安全
若是線程一開始就被標記爲中斷狀態,再調用 lockInterruptibly() 方法,lockInterruptibly() 方法則會直接拋出中斷異常。微信
4. tryLock(long timeout, TimeUnit unit) 方法(可被中判定時鎖)
tryLock(long timeout, TimeUnit unit) 也區分公平鎖和非公平鎖;與 lockInterruptibly() 方法的區別就在於:線程調用 tryLock(long timeout, TimeUnit unit) 方法,獲取不到鎖,進入阻塞後;若是在指定的時間裏,仍然沒有被其它釋放鎖的線程喚醒,則會自動喚醒,直接返回失敗。
知足如下幾個特色,咱們就說這個操做支持原子性,線程安全:
解釋:
包含多個操做單元,但仍支持原子性,一般都是由鎖實現的。
代碼示例:
class Test { int x = 0; int y = 0; public void test() { // 原子操做 x = 10; // 大體分爲兩步:1)獲取 x 的值到緩存裏;2)取出緩存裏的值,賦值給 y // 不支持原子性;獲取 x 的值到緩存裏以後,其它線程可能修改 x 的值,致使 y 值錯誤 y = x; // 大體分爲三步:1)獲取 x 的值到緩存裏;2)取出緩存裏的值加一;3)賦值給 x // 不支持原子性;原理相似 y = x; x++; } }
Cas 是 Compare-and-swap(比較並替換)的縮寫,是支持原子性的操做;在 Java 中,底層是 native 方法實現,經過 CPU 提供的 lock 信號保證的原子性。
想要將數據 V 的原值 O 替換爲新值 N,執行 Cas 操做,會有如下操做:
執行 Cas 操做:
比較當前數據 V 的值是不是 O;
例子:
以 java.util.concurrent.atomic 包中的 AtomicInteger 爲例;
public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(100); // 將 AtomicInteger 的值,從 100 替換爲 200 Boolean b = atomicInteger.compareAndSet(100, 200); // 返回 true,替換成功 System.out.println(b); }
用於修飾變量,能夠保證被修飾變量的操做支持可見性和有序性,但不支持原子性。詳見」Java 併發面試題集「
void interrupt():在一個線程中調用另外一個線程的 interrupt() 方法,會將那個線程設置成線程中斷狀態,而不會真的中斷線程。
boolean isInterrupted(boolean ClearInterrupted):返回當前線程是否處於中斷狀態,ClearInterrupted 爲 true,則返回的同時清除中斷狀態,反之則保留中斷狀態。
interrupt() 代碼示例
class ThreadTest { public static void main(String[] args) throws Exception { MyThread thread = new MyThread(); thread.start(); // 主線程經過調用 thread 對象的 interrupt() 方法,將 thread 線程設置成線程中斷狀態,但不會真的中斷線程。 thread.interrupt(); System.out.println("主線程執行結束!"); } } class MyThread extends Thread { @Override public void run() { System.out.println("test thread!"); } } // 打印結果: // 主線程執行結束! // test thread!
在 ReentrantLock 中,定義了一個內部抽象類 Sync;有兩個實現類,分別爲 FairSync(公平鎖) 和 NonfairSync (非公平鎖),是 ReentrantLock 裏的真正實現鎖邏輯的類。
代碼示例 1 ReentrantLock 成員變量
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { // state 表示鎖的狀態。int 類型未指定值,默認初始化爲0; // 爲 0 則表明當前沒有線程持有鎖;爲 1 則表明有線程持有鎖;若是大於1,則表明鎖被當前線程重入的次數 // 繼承自 AbstractQueuedSynchronizer private volatile int state; // exclusiveOwnerThread 表示當前持有鎖的線程對象 // 繼承自 AbstractOwnableSynchronizer private transient Thread exclusiveOwnerThread; // tail 記錄尾線程節點對象,默認爲 null // 繼承自 AbstractQueuedSynchronizer private transient volatile Node tail; // head 記錄頭線程節點對象,默認爲 null // 繼承自 AbstractQueuedSynchronizer private transient volatile Node head; }
ReentrantLock 使用 FIFO (先進先出)隊列來管理競爭鎖的線程關係。
在 ReentrantLock 中, Sync 經過繼承了 AQS 抽象類(AbstractQueuedSynchronizer),進而繼承 AbstractQueuedSynchronizer 中的內部類 Node,用來封裝線程,同時也是鏈表的組成元素。
代碼示例 Node 重要成員變量
static final class Node { // prev 指向前一個節點(表明前一個進入鏈表的線程) volatile Node prev; // next 指向後一個節點(表明後一個進入鏈表的線程) volatile Node next; // thread 當前節點表示的線程 volatile Thread thread; // 0:表示[初始默認值]或者[表示已解鎖] // -1(SIGNAL):表示當前節點表明的線程在釋放鎖後須要喚醒下一個節點的線程 // 1(CANCELLED):表示當前節點表明的線程在隊列中發生異常(發生中斷異常或者其它不可預知的異常),標記爲取消狀態 volatile int waitStatus;
初始狀態:head 和 tail 變量爲空,不存在鏈表。
初始化不表明線程的頭節點,再在頭節點後插入線程 1 節點(線程 1 獲取鎖失敗,放入鏈表,等待獲取鎖):
而後再爲線程 1 新建一個 Node 節點對象 node1,追加到頭節點後面:
線程 1 節點替換成頭節點(線程 1 在鏈表中被線程 0 喚醒,成功獲取到鎖):
<br/>
加入個人粉絲社羣,閱讀所有內容
從學習到面試,從面試到工做,從 coder 到 TeamLeader,天天給你答疑解惑,還能有第二份收入,這樣的知識星球,難道你還要猶豫!