多线程并发tomcat

从tomcat任务队列看ReentrantLock非公平与公平锁

2021-11-05  本文已影响0人  肥兔子爱豆畜子

tomcat的线程池用的任务队列 TaskQueue extends LinkedBlockingQueue<Runnable>,可见是个阻塞队列,里边poll,take这种方法出队之前先要拿锁,源码可以看到是用的ReentrantLock , takeLock = new ReentrantLock()是非公平锁。

ReentrantLock是基于AQS实现的,内部有两个内部类NonfairSyncFairSync,继承自Sync,而Sync是继承自AQS的。

我们来走读一下这两个内部类:
非公平锁实现

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1)) //尝试先来一次CAS抢锁
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1); //没拿到就正常acquire,里边是!tryAcquire(arg) && acquireQueued
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires); //里边看如果锁0,那么再来一次CAS、成功则返回true;或者锁持有者是当前线程,则重入;其他情况返回false
    }
}

公平锁实现:

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

    final void lock() {
        acquire(1); //正常acquire,里边是!tryAcquire(arg) && acquireQueued
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&  //无前序节点
                compareAndSetState(0, acquires)) { //CAS
                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;
    }
}
总结

LinkedBlockingQueue.poll的时候如果是非公平锁,每个抢锁的线程都可以去尝试CAS先搞2把,没成功则acquireQueued。
如果是公平锁,则先要判断当前线程在同步队列里没有前序节点才可以CAS尝试1次,如果没成功则acquireQueued。

上一篇下一篇

猜你喜欢

热点阅读