Java中ReentrantLock的公平锁和非公平锁

一、背景知识

       了解 Java 中 ReentrantLock 的程序员都知道,ReentrantLock 的核心组成是队列同步器 AbstractQueuedSynchronizer,它采用的模板方法的设计模式,即使用者需要继承同步器并重写指定的方法,而同步器中的一些模板方法将会调用这些方法。需要自定义的方法有下面几个:

//独占式获取同步状态
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
//独占式释放同步状态
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
//共享式获取同步状态
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
//共享式释放同步状态
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}
//是否被当前线程独占
protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}

       而在 ReentrantLock 类的内部,定义了一个抽象类 Sync,看代码:

public class ReentrantLock implements Lock {
    ...
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer
    {...}
    ...
}

在 Sync 的内部有几个比较重要的方法:

abstract static class Sync extends AbstractQueuedSynchronizer {
    abstract void lock();
    final boolean nonfairTryAcquire(int acquires) {...}
    protected final boolean tryRelease(int releases) {...}
}

       ReentrantLock 内部还定义了两个类并继承了抽象类 Sync,见代码。顾名思义,这两个类分别代表了 ReentrantLock 的两种锁模式:公平锁和非公平锁。

static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}

       接下来分析 ReentrantLock 的公平锁和非公平锁。

二、非公平锁

       ReentrantLock 默认情况下是构造非公平锁,但也提供了一个可供选择的构造函数,代码一目了然:

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

       加锁方式如下:

public void lock() {
    //这里的lock()方法就是Sync内部类重定义的抽象方法,见上文。
    sync.lock();
}

       lock( ) 方法在子类 FairSync 和 NonfairSync 中实现了。先看非公平锁:

static final class NonfairSync extends Sync {
    final void lock() {
        //首先原子性设置同步状态
        if (compareAndSetState(0, 1))
            //成功的话将独自占有锁的线程设为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //否则,执行模板方法 void acquire(int arg)
            acquire(1);
    }
    //模板方法void acquire(int arg)中调用了boolean tryAcquire(int arg)方法
    protected final boolean tryAcquire(int acquires) {
        //nonfairTryAcquire(int arg)是在Sync抽象类中定义的
        return nonfairTryAcquire(acquires);
    }
}

       模板方法的定义是固定好的:

public final void acquire(int arg) {
    //尝试获取同步状态,失败的话添加节点到同步队列中
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

       所以重点分析 Sync内部类中的 boolean nonfairTryAcquire(int acquires) 方法:

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()) {
        //c不为0的话说明其他线程正在占有锁,此时判断是否为当前线程获取了锁
        //是的话就可以获取同步状态,这也是可重入锁的关键
        int nextc = c + acquires;
        if (nextc < 0) 
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //否则获取同步状态失败
    return false;
}

       比较一下公平锁的代码就只带差异了。

三、公平锁

       直接上代码:

static final class FairSync extends Sync {
    final void lock() {
        //此处没有直接去设置state同步状态,而是调用了模板方法。
        //模板方法调用了下面的boolean tryAcquire(int acquires)方法
        acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {    //差异在这里
            /*当前没有其他线程获取到同步状态时,不是直接去设置state
             *而是检查当前线程所在的节点是否有前驱节点(同步队列
             *是一个FIFO队列,存在前驱节点的话说明有线程更早地
             *尝试获取同步状态),没有前驱节点的话才去设置state
             */
            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;
    }
}

       从代码可以看出公平锁和非公平锁的差异了。差异就是:在 compareAndSetState(0, acquires)) 的时候是否去检查有没有更早的获取锁的线程。公平锁秉承着“先到先得”的理念,而非公平锁则不管这么多,只要是当前 state 为0,就去尝试获取。

四、最后

        ReentrantLock的精髓远不于此,要想理解好ReentrantLock,就必须掌握 AbstractQueuedSynchronizer,同步器的分析在后面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值