ReentrantLock 重入锁

解决了什么问题

重入锁解决了同步方法调用另一个同步方法时死锁的问题(即方法A没有解锁的情况下 方法B可以取得锁 并在B归还锁之后 锁依然被A持有)

 

代码示例:

以下代码中使用两种重入方式  关键字synchronized 和 基于AQS的重入锁ReentrantLock 

public static void main(String[] args) throws InterruptedException {
    Test test = new Test();
    test.testA();
    test.testC();

}

//inner class
public static class Test{

    ReentrantLock lock = new ReentrantLock();

    //使用synchronized重入锁
    public synchronized void testA(){
        System.out.println("testA");
        testB();
    }
    private synchronized void testB(){
        System.out.println("testB");
    }

    //使用ReentrantLock重入锁
    public void testC(){
        lock.lock();
        System.out.println("testC");
        testD();
        lock.unlock();
    }
    private void testD(){
        lock.lock();
        System.out.println("testD");
        lock.unlock();
    }

}

代码分析:

test.testA() 使用synchronized 关键字, 进入方法可知, testA方法取得了test的对象锁(不太明白可以看我之前synchronized 文章), 然后在方法testA中调用testB方法, 此时对象锁未被释放, 但是synchronized 属于重入锁, 同一个线程中的testB依然可以获取test对象锁

test.testC() 使用ReentrantLock锁,同样达到同一线程重入的目的, 具体源码解析接下来讲

 

ReentrantLock源码分析:

lock()方法: 获取锁, 原理比较简单, 若没有线程占有锁则之间独占; 再判断是否有线程占有锁则判断是不是当前线程占有了, 若是当前线程占有则, 将AQS的state加1, 若非当前线程则假如AQS等待队列 

类 ReentrantLock
//获取锁
public void lock() {
    sync.lock();
}

类 ReentrantLock.NonfairSync
final void lock() {
    //尝试原子操作获取锁 这是非公平锁特性 先尝试获取锁 获取不到再做判断
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    //没有获取到锁
    else
        acquire(1);
}
//获取锁
public final void acquire(int arg) {
    //若是当前线程获取锁 tryAcquire(arg)=true则直接走完代码
    //若非当前线程获取锁 tryAcquire(arg)=false 则会acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 将当前线程假如等待锁队列
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
//尝试获取锁
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
//非公平锁尝试获取实现
final boolean nonfairTryAcquire(int acquires) {
    //获取当前线程
    final Thread current = Thread.currentThread();
    //获取当前AQS状态
    int c = getState();
    //若当前状态为0 表示没有线程占有锁 则原子操作获取锁
    //并将AQS独占线程设置为当前线程
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            //AQS独占线程设置为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果c!=0,即有线程获取了锁 则判断获取到锁的线程是否为当前线程
    else if (current == getExclusiveOwnerThread()) {
        //预期设置AQS的state=state+acquires 在这里acquires值为1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        //设置AQS状态为state=state+acquires
        setState(nextc);
        //返回获取锁成功
        return true;
    }
    return false;
}

 

 

unlock方法: 释放锁, 原理是利用AQS的state和exclusiveOwnerThread来判断, 若非当前线程释放锁直接抛出异常;  若是当前线程操作则需要对比state, 通俗的说就是在同一个线程中每一次lock()方法则state加1 每一次unlock()方法state减1

知道unlock()次数和lock()次数一样, state的值重新变成0, 则释放锁, 并将锁让给下一个等待中的线程

类 ReentrantLock
//释放锁
public void unlock() {
        sync.release(1);
    }

类 ReentrantLock.Sync

//释放锁
public final boolean release(int arg) {
    //tryRelease(arg)解释如下
    //若是当前线程释放锁, 且AQS状态state等于0,tryRelease(arg)等于true 则唤醒当前线程,并通知后面等待线程
    //若是当前线程释放锁, 且AQS状态state不等于0,tryRelease(arg)等于false 则只是将state减1
    //若是非当前线程释放 抛出异常
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
    //c=state-1
    int c = getState() - releases;
    //若当前释放锁的线程非独占此锁的线程 则抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;

    //如果state-1==0 则释放成功, 并设置独占线程为空, 以便下一个线程独占此锁
    //若是重入锁 那么state-1!=0 则独占线程任然为当前线程 其他线程依然无法获取锁
    if (c == 0) {
        free = true;
        //设置当前独占线程为空
        setExclusiveOwnerThread(null);
    }
    //设置当前state=c=state-1
    setState(c);
    //若是重入 c!=0 则free任然是false
    return free;
}

 

 

总结:作为AQS的高级应用, 实现上并不复杂, 只是在AQS的基础上做了state的判断

posted @ 2020-01-08 14:51  蟹烟客  阅读(184)  评论(0编辑  收藏  举报