引言
提起java的線程同步敬辣,大家總能想到sychronized關(guān)鍵字。sychronized是由JVM提供的重量級(jí)鎖溉跃,使用方式簡(jiǎn)單撰茎,功能比較單一。
ReentrantLock是由java API提供的用來做線程同步的類,它的實(shí)現(xiàn)借助了隊(duì)列同步器AQS(AbstractQueuedSynchronizer)募疮。
AQS 是 Java 并發(fā)包中實(shí)現(xiàn)鎖僻弹、同步的一個(gè)重要基礎(chǔ)框架蹋绽。
ReentrantLock提供了比sychronized更豐富的功能,包括可重入卸耘、公平鎖蚣抗、可中斷等。
ReentrantLock使用
ReentrantLock的使用姿勢(shì)如下
private ReentrantLock lock = new ReentrantLock();
public void run() {
lock.lock();
try {
//do bussiness
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
公平鎖
實(shí)例化ReentrantLock時(shí)设哗,可以通過構(gòu)造器傳參指定聲明的是公平鎖還是非公平鎖两蟀。
Lock lock=new ReentrantLock(true);//公平鎖
Lock lock=new ReentrantLock(false);//非公平鎖
公平鎖是指線程獲取鎖的順序是按照加鎖順序來的,而非公平鎖則可以搶鎖战虏,先申請(qǐng)的線程不一定先獲得鎖党涕。
由于公平鎖需要維護(hù)一個(gè)隊(duì)列,記錄當(dāng)前已排隊(duì)的線程手趣,所以非公平鎖的性能會(huì)高于公平鎖肥荔。默認(rèn)構(gòu)造函數(shù)創(chuàng)建的就是非公平鎖。
public ReentrantLock() {
sync = new NonfairSync();
}
tryLock()
tryLock的功能非常實(shí)用中符,實(shí)際業(yè)務(wù)中的線程等待鎖資源釋放不是永無止境誉帅。通過tryLock形參可以設(shè)定等待時(shí)間右莱,時(shí)間到后如果能夠獲取鎖就返回true慢蜓,否則返回false阀捅,而不是一直阻塞直到等待的鎖資源被釋放。
Lock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.tryLock(100, TimeUnit.MILLISECONDS);
} finally {
lock.unlock();
}
}
ReentrantLock原理
查看源碼凄诞,可以看到ReentrantLock類包含了3個(gè)內(nèi)部類:
- Sync:其中抽象內(nèi)部類Sync繼承自AQS忍级,實(shí)現(xiàn)最核心的功能。
- FairSync:繼承自抽象類Sync汛蝙,實(shí)現(xiàn)公平鎖
- NonfairSync:繼承自抽象類Sync朴肺,實(shí)現(xiàn)非公平鎖
加鎖解析
公平鎖
ReentrantLock的lock()方法調(diào)用的是Sync的lock方法戈稿,lock()是抽象方法,具體實(shí)現(xiàn)由其子類FairSync的lock方法提供需了。
//ReentrantLock的lock方法
public void lock() {
sync.lock();
}
//FairSync的lock方法
final void lock() {
acquire(1);
}
acquire方法來自Sync類
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire方法的公平鎖實(shí)現(xiàn)如下
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
state字段為0表示沒有被任何線程持有般甲,緊接著調(diào)用hasQueuedPredecessors方法判斷隊(duì)列中是否有其他線程正在排隊(duì)
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
如果沒有排隊(duì)的線程敷存,就調(diào)用CAS的compareAndSetState方法線程安全的將同步器的state字段設(shè)置為1。接下來調(diào)用setExclusiveOwnerThread方法把當(dāng)前線程保存下來觅闽,以便下次該線程的重入挽牢。
state大于0意味著當(dāng)前鎖已經(jīng)被線程持有摊求,需要判斷持有鎖是否當(dāng)前線程。
如果是當(dāng)前線程睹栖,將state值加一重新寫入state狀態(tài)(線程可重入)。
如果不是當(dāng)前線程恼除,直接返回失敗曼氛。
acquire方法中,tryAcquire方法返回false會(huì)繼續(xù)調(diào)用addWaiter(Node.EXCLUSIVE), arg)方法將當(dāng)前線程構(gòu)建成Node對(duì)象寫入到線程排隊(duì)隊(duì)列徽级。
AQS中的線程隊(duì)列是由Node對(duì)象為節(jié)點(diǎn)的雙向鏈表聊浅。Node節(jié)點(diǎn)包含兩種模式:排他模式與共享模式低匙。ReentrantLock是排他模式,ReadWriteLock是共享模式顽冶。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
如果該隊(duì)列已經(jīng)有node(tail!=null)渗稍,則將新節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)置為tail,再通過CAS將tail指向當(dāng)前節(jié)點(diǎn)报强,前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)拱燃,然后返回當(dāng)前節(jié)點(diǎn)。
如果隊(duì)列為空或者CAS失敗召嘶,則通過enq入隊(duì)哮缺。
進(jìn)隊(duì)列的時(shí)候,要么是第一個(gè)入隊(duì)并且設(shè)置head節(jié)點(diǎn)并且循環(huán)設(shè)置tail铛只,要么是add tail,如果CAS不成功直撤,則會(huì)無限循環(huán)蜕着,直到設(shè)置成功,即使高并發(fā)的場(chǎng)景蓖乘,也最終能夠保證設(shè)置成功悄雅,然后返回包裝好的node節(jié)點(diǎn)。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先根據(jù) node.predecessor() 獲取到上一個(gè)節(jié)點(diǎn)是否為頭節(jié)點(diǎn)众眨,如果是則嘗試獲取一次鎖娩梨,獲取成功就完成任務(wù)了览徒。
如果不是頭節(jié)點(diǎn),或者獲取鎖失敗纽什,則會(huì)根據(jù)上一個(gè)節(jié)點(diǎn)的waitStatus狀態(tài)來處理躲叼。shouldParkAfterFailedAcquire(p,node) 返回當(dāng)前線程是否需要掛起,如果需要?jiǎng)t調(diào)用 parkAndCheckInterrupt()让蕾。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
parkAndCheckInterrupt方法使用LockSupport的park方法掛起線程或听。
非公平鎖
//ReentrantLock的lock方法
public void lock() {
sync.lock();
}
//NonfairSync的lock方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
非公平鎖直接調(diào)用compareAndSetState方法修改state,修改成功即成功獲得鎖誉裆,將當(dāng)前線程寫入排他鎖獨(dú)占線程。如果獲取鎖失敗粱腻,則調(diào)用Sync的acquire方法,非公平鎖的tryAcquire方式實(shí)現(xiàn)如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
非公平鎖的邏輯相對(duì)簡(jiǎn)單,不需要判斷線程隊(duì)列直接嘗試獲得鎖遇革。
釋放鎖解析
公平鎖和非公平鎖的釋放流程都是一樣的揭糕。
public void unlock() {
sync.release(1);
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
總結(jié)
由于公平鎖維護(hù)了線程隊(duì)列著角,加鎖的流程比較繁瑣導(dǎo)致性能開銷大。實(shí)際使用中可優(yōu)先考慮非公平鎖奄容,以獲取更好的性能優(yōu)勢(shì)产徊。