人海原始碼系列 Reentrantlock

2021-10-04 12:12:17 字數 3636 閱讀 3600

今天講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...