ReentrantLock可重入鎖的理解和源碼簡單分析

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author admin
 * @date 2018/1/16 12:16
 * ReentrantLock 可重入鎖:
 * 一、啓動兩個線程,線程A獲取鎖,而後執行;同時線程B進來後,一直阻塞,直到線程A釋放鎖以後,線程B才接着執行
 */
public class ReentrantLockTest {
    ReentrantLock lock = new ReentrantLock();

    public void reentrantLockRun1(String threadName) {
        System.out.println(threadName + "進入");
        lock.lock();
        System.out.println(threadName + "方法被鎖");
        try {
            System.out.println(threadName + "方法執行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(threadName + "鎖被釋放");
        }
    }

    public static void main(String[] args) {
        ReentrantLockTest rltest = new ReentrantLockTest();
        Thread thread = new Thread() {
            public void run() {
                rltest.reentrantLockRun1("線程A");
            }
        };
        thread.start();

        Thread thread2 = new Thread() {
            public void run() {
                rltest.reentrantLockRun1("線程B");
            }
        };
        thread2.start();
    }
}

運行結果:
線程A進入
線程A方法被鎖
線程A方法執行
線程B進入
線程A鎖被釋放
線程B方法被鎖
線程B方法執行
線程B鎖被釋放

 

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author admin
 * @date 2018/1/16 12:16
 * ReentrantLock 可重入鎖:
 * 一、啓動兩個線程,線程A獲取鎖,而後執行,不釋放鎖,接着線程A再調用reentrantLockRun2,不須要阻塞,接着執行,最後釋放鎖;說明同一個線程對ReentrantLock可重複獲取
 * 二、線程B在這個過程當中一直阻塞,等到線程A把全部的鎖釋放完以後,再獲取鎖,執行方法,最後釋放鎖
 */
public class ReentrantLockTest2 {
    ReentrantLock lock = new ReentrantLock();

    public ReentrantLock reentrantLockRun1(String threadName) {
        System.out.println(threadName + "進入");
        lock.lock();
        System.out.println(threadName + "方法被鎖");
        try {
            System.out.println(threadName + "方法執行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return lock;
    }

    public ReentrantLock reentrantLockRun2(String threadName) {
        System.out.println(threadName + "進入");
        lock.lock();
        System.out.println(threadName + "方法被鎖");
        try {
            System.out.println(threadName + "方法執行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return lock;
    }

    public static void main(String[] args) {
        ReentrantLockTest2 rltest = new ReentrantLockTest2();
        Thread thread = new Thread() {
            public void run() {
                ReentrantLock lock1 = rltest.reentrantLockRun1("線程A");
                ReentrantLock lock2  = rltest.reentrantLockRun2("線程A2");
                lock1.unlock();
                System.out.println("線程A釋放鎖");
                lock2.unlock();
                System.out.println("線程A2釋放鎖");

            }
        };
        thread.start();

        Thread thread2 = new Thread() {
            public void run() {
                ReentrantLock lock = rltest.reentrantLockRun1("線程B");
                lock.unlock();
                System.out.println("線程B釋放鎖");

            }
        };
        thread2.start();
    }
}

運行結果:
線程A進入
線程A方法被鎖
線程A方法執行
線程B進入
線程A2進入
線程A2方法被鎖
線程A2方法執行
線程A釋放鎖
線程A2釋放鎖
線程B方法被鎖
線程B方法執行
線程B釋放鎖

 

根據源碼發現:維護了這個可見性變量state ;同一個線程對可重入鎖體現用state標記做累加,int nextc = c + acquires;java

private volatile int state;
node

 

public void lock() {
sync.lock();
}

 

 

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

// 判斷是否是第一次獲取鎖,若是是操做state=1;不然判斷是否是同一個線程若是是state+1,若是不是同一個線程直接返回false
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;
}
}
// 大概就是用一個鏈表來維護等待線程
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);
}}
相關文章
相關標籤/搜索