android

多线程知识梳理(12) - ReentrantLock 解析

2019-05-10  本文已影响15人  泽毛

一、ReentrantLock

ReentrantLockJava提供的并发同步组件,内部也是基于队列同步器AQS来实现的,它具有两个特点:

1.1 重进入

重进入需要解决两个问题:

ReentrantLock内部声明了两个内部类FairSyncNonfairSync,它们都继承于AbstractQueuedSynchronizer,分别对应公平锁和非公平锁的实现。我们以NonfairSync来看一下它是如何实现重进入的功能,为了便于理解这里只保留了和加锁相关的部分。

public class ReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
    
        abstract void lock();

        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;
        }
        
        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;
        }

    }

    static final class NonfairSync extends Sync {

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
    public void lock() {
        sync.lock();
    }
    
    public void unlock() {
        sync.release(1);
    }
    
}    

lock的流程如下:

unlock的流程如下:

1.2 公平锁与非公平锁

公平性是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序应当符合请求的绝对时间顺序,也就是FIFO

ReentrantLock提供了公平锁和非公平锁的选择,从实现上来说,就是组合的队列同步器Sync的类型是FairSync还是NonfairSync,非公平锁的实现我们之前已经分析过了,下面来看一下公平锁的实现。

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            //区别一:没有直接根据当前同步状态返回。
            acquire(1);
        }

        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;
        }
    }

这里的区别就是在没有任何线程获取到同步状态的时候,加入了是否有前驱结点的判断,如果该函数返回true,那么表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁后才能继续获取锁。

1.3 对比

1.4 API 接口

由于ReentrantLock实现了Lock接口,因此其API的定义和Lock的定义相同:

这里比较容易混淆的是 响应中断 的概念,中断的情况分为两种:

    
    //1.不响应中断的处理方式。
    final boolean acquireQueued(final Node node, long arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null;
                    return interrupted;
                }
                //在同步队列中遇到中断会被唤醒。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //仅仅是设置表示位。
                    interrupted = true;
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }
    
    //2.响应中断的处理方式。
    private void doAcquireInterruptibly(long arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null;
                    return;
                }
                //在同步队列中遇到中断会被唤醒。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //抛出异常。
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }
    
上一篇下一篇

猜你喜欢

热点阅读