《Java高併發程式設計》 --通過ReentrantLock觀望Java鎖機制
阿新 • • 發佈:2021-01-08
多執行緒鎖有兩種:
一種是用關鍵字 : syncronized實現
另一種是用Lock的實現類實現。 關於syncronized的解釋可以參考一位博主寫的(https://www.cnblogs.com/yuhangwang/p/11295940.html)
這裡就先看ReentrantLock的實現,去窺探java的鎖機制。
首先整體流程圖如下:
示例程式碼:
public class TestTryLock implements Runnable { private static Lock locks = new ReentrantLock(); @Overridepublic void run() { try { if(locks.tryLock(4, TimeUnit.SECONDS)){ // lock.lock() System.out.println(Thread.currentThread().getName()+"-->"); Thread.sleep(3000); }else{ System.out.println(Thread.currentThread().getName()+" time out "); } }catch (InterruptedException e) { // e.printStackTrace(); }finally { locks.unlock();//會丟擲鎖物件的異常,因為沒有獲取鎖在unlock的時候出異常,可以先判斷一下是否存在在執行。 } } public static void main(String[] args) throws InterruptedException { TestTryLock test =new TestTryLock(); Thread t1= new Thread(test,"t1"); Thread t2 = new Thread(test,"t2"); Thread t3 = new Thread(test,"t3"); t1.start(); t2.start(); t1.join(); t2.join(); } }
示例用使用的是 ReentrantLock的非公平鎖的實現,即執行緒不需要按照申請資源的順序進行搶佔鎖資源。
通過使用lock.lock() 和lock.unlock()的配合,進行對資源的加解鎖。
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
lock.lock() 是執行緒對資源進行繫結的操作,當執行緒佔有了該共享資源(lock)的時候,會在 AQS同步器中的
exclusiveOwnerThread 屬性設定當前搶佔執行緒的值。 並將state設定為 1 。
如果沒有搶佔到資源的時候,就會走acquire(1)的路子。 也就是會將沒有搶到鎖的執行緒丟進等待佇列中,這是一個FIFO佇列
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
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;
}
然後不斷的進行自旋等待,判斷前一節點是不是頭節點,當原先搶佔了資源鎖的執行緒進行lock.unlock()等操作時,
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
也就是呼叫了AQS的release操作。 就會將state - 1 處理,一旦state == 0,則就說明可以釋放鎖了。
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; }
如果等待佇列中某節點的前一節點是頭節點,該節點就開始申請搶佔 AQS中的
exclusiveOwnerThread。 如果成功了,就離開等待佇列,進入到執行狀態。
以上。