今天講reentrantlock原始碼,講解的思路是先自己手寫最簡單lock,然後一步一步增加功能,然後再去看原始碼
目錄
手寫版本1,最基礎的lock,搶不到鎖就自旋
手寫版本2,搶不到鎖就把自己放入佇列並且阻塞自己
先寫基礎部分,獲取unsafe類和stateoffset,這個主要是也用於cas操作
private final static unsafe unsafe = unsafeutils.getunsafe();//1. 獲取unsafe類
private final static long stateoffset; //2. 定義偏移量
static catch (nosuchfieldexception e)
}
接著寫lock方法和unlock方法,並且定義乙個int型別的變數state當做鎖
private int state = 0;
public void lock() catch (interruptedexception e) }}
public void unlock()
這樣,乙個最最簡單的lock就完成了,讓我們測試一下
public static void main(string args) throws interruptedexception
});thread t2 = new thread(() ->
});t1.start();
t2.start();
t1.join();
t2.join();
system.out.println(k);
}
輸出結果
connected to the target vm, address: '127.0.0.1:65421', transport: 'socket'
112277
disconnected from the target vm, address: '127.0.0.1:65421', transport: 'socket'
process finished with exit code 0
然後再看加上lock的測試結果
public static void main(string args) throws interruptedexception
});thread t2 = new thread(() ->
});t1.start();
t2.start();
t1.join();
t2.join();
system.out.println(k);
}
測試結果
connected to the target vm, address: '127.0.0.1:59131', transport: 'socket'
200000
disconnected from the target vm, address: '127.0.0.1:59131', transport: 'socket'
process finished with exit code 0
測試結果證明我們的lock生效了,但是這個版本的lock存在乙個問題,就是當搶不到鎖的時候會進入自旋,如果搶到鎖的任務耗時太長,那麼就會浪費很多cpu資源,所以我們要增加乙個佇列用於存放搶不到鎖的執行緒,並且用locksupport.park()方法阻塞它,然後解鎖的時候去喚醒佇列中的執行緒,這樣在獲取鎖線程執行時間再長也不會浪費資源,因為搶不到鎖的執行緒是在佇列中阻塞著的.
lock方法的改動
unlock方法的改動
整體思路:增加乙個變數存放持有鎖的執行緒,當持有鎖的執行緒獲取鎖時直接把state+1,持有鎖的執行緒要釋放鎖的時候就把state-1
private int state = 0;
private linkedblockingdequewaiters = new linkedblockingdeque<>();
//持有鎖的執行緒
private thread holdthread;
public void lock()
locksupport.park(); // 搶鎖失敗,阻塞自己,等待被喚醒}}
}public boolean trylock()
if (unsafe.compareandswapint(this, stateoffset, 0, 1))
return false;
}public void unlock()
}
測試**
public static void main(string args) throws interruptedexception catch (interruptedexception e)
lock.unlock();
logger.info("解鎖");
});thread t2 = new thread(() -> );
t1.start();
t2.start();
t1.join();
t2.join();
}
測試結果,執行緒1是等執行緒0釋放兩次鎖才搶到鎖的,釋放第二次鎖之前執行緒阻塞1秒鐘特意給執行緒1留出時間,然而那時候執行緒0還沒釋放完畢,還持有一把鎖,所以即使阻塞一秒鐘也沒辦法搶到鎖
connected to the target vm, address: '127.0.0.1:51910', transport: 'socket'
12:37:48.362 [thread-0] info com.lz.demo.concurrent.renhai.renhailockv2reentrant - 搶到鎖
12:37:48.366 [thread-0] info com.lz.demo.concurrent.renhai.renhailockv2reentrant - 搶到鎖
12:37:48.366 [thread-0] info com.lz.demo.concurrent.renhai.renhailockv2reentrant - 解鎖
12:37:49.369 [thread-0] info com.lz.demo.concurrent.renhai.renhailockv2reentrant - 解鎖
12:37:49.369 [thread-1] info com.lz.demo.concurrent.renhai.renhailockv2reentrant - 搶到鎖
0disconnected from the target vm, address: '127.0.0.1:51910', transport: 'socket'
process finished with exit code 0
HashMap原始碼系列
為了提高自己的 水平和除錯水平 主要還是面試會問 想哭 逼著自己看了主要集合框架的原始碼 這裡主要是hashmap 當然整個過程也是辛苦的 我們都知道集合的本質是資料結構 博主為了看懂它真的是下足了功夫 我的方法是 測試特殊的例子 idea的debug很強大 可以通過它看看 是否會根據自己所想的那樣...
Eureka原始碼系列 2 原始碼啟動入口
本文介紹如何搭建eureka原始碼的debug環境,網上有一些部落格抄來抄去,抄錯了都不知道。eureka server模組下有乙個測試類 eureka server src test j a com netflix eureka resources eurekaclientserverrestin...
Redis原始碼分析系列
redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...