ReentrantLock源码解读

ReentrantLock源码分析

这次来阅读一下ReentrantLock的源码,简单阅读,只看几个重要的方法。
首先ReentrantLock实现了Lock接口,Lock接口没啥好说的,提供了几个必须实现的方法。

public interface Lock {
    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

lock接口给出了必须实现的加锁和解锁的方法,来看看ReentrantLock是如何实现的。
首先,ReentrantLock可以通过构造方法传bool值来创建公平锁或非公平锁。

	private final ReentrantLock.Sync sync;

    public ReentrantLock() {
        this.sync = new ReentrantLock.NonfairSync();
    }

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

可以看到,ReentrantLock有一个内部类Sync的引用,加锁和解锁的过程其实是由sync指向的对象来完成,无参构造函数将sync指向非公平锁的对象,带参数构造传入true则是公平锁对象,false则是非公平锁。因此,ReentrantLock默认是非公平锁。

再看其对Lock接口方法的实现。

	public void lock() {
        this.sync.acquire(1);
    }

    public void lockInterruptibly() throws InterruptedException {
        this.sync.acquireInterruptibly(1);
    }

    public boolean tryLock() {
        return this.sync.nonfairTryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        return this.sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    public void unlock() {
        this.sync.release(1);
    }

    public Condition newCondition() {
        return this.sync.newCondition();
    }

可以看到,每个方法的实现都是调用this.sync这个内部类实现的。先看一下Sync这个内部类的结构。

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        Sync() {
        }

        @ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = this.getState();
            if (c == 0) {
                if (this.compareAndSetState(0, acquires)) {
                    this.setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == this.getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(nextc);
                return true;
            }

            return false;
        }

        @ReservedStackAccess
        protected final boolean tryRelease(int releases) {
            int c = this.getState() - releases;
            if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            } else {
                boolean free = false;
                if (c == 0) {
                    free = true;
                    this.setExclusiveOwnerThread((Thread)null);
                }

                this.setState(c);
                return free;
            }
        }
    }

可以看到,Sync继承了AbstractQueuedSynchronizer这个类,也就是神秘的AQS(抽象队列同步器),AQS类似与一个模板,封装了Lock接口下各种锁实现的公共方法。先作了解,下一章节再看看AQS是个什么东西。这里只列举了两个重要的方法,nonfairTryAcquire()tryRelease()。分别对应与Lock接口的tryLock()tryUnLock()

非公平锁

先看看nonfairTryAcquire方法

int c = this.getState()	//获取锁重入的次数
if(c == 0)	//如果重入次数为0,也就是锁没有被任何线程占有
	this.compareAndSetState(0, acquires)	//使用CAS修改重入次数,CAS(0-->1)
	this.setExclusiveOwnerThread(current)	//CAS修改成功后,修改锁标记为当前线程并返回true
if(c != 0)	//如果重入次数不为0,即已有线程占有锁
	if (current == this.getExclusiveOwnerThread())	//如果占有锁的线程是当前线程,就重入锁
	int nextc = c + acquires;	//重入后,重入次数将修改为nextc
    	if (nextc < 0) {	//如果c超出Int.MaxValue,nextc可能为负数,此时应报错
        	throw new Error("Maximum lock count exceeded");
        }
    this.setState(nextc);	//修改state重入次数,返回true

这就是ReentrantLock的tryLock()方法源码的实现。其实Lock加锁就是不断的tryLock尝试抢锁的过程。抢锁的过程中有两个属性是重点,重入次数state和锁被占有的线程exclusiveOwnerThread。简单来说就是:

  1. 判断重入次数state是否为0
  2. state为0,说明没有线程占有锁,通过CAS操作修改state
  3. 修改state成功后,即抢锁成功,马上修改exclusiveOwnerThread为当前线程,返回true
  4. 如果state不为0,说明已有线程占有锁,判断这个线程是否是当前线程,若不是则加锁失败,返回false
  5. 若这个线程是当前线程,则重入锁,即修改state重入次数加1,返回true

再看看tryRelease方法

int c = this.getState() - releases;	//解锁后重入的次数
if (Thread.currentThread() != this.getExclusiveOwnerThread())	//如果占有锁的线程不是当前线程,则报错
//否则
if(c == 0)	//锁释放完毕
	this.setExclusiveOwnerThread((Thread)null);		//修改锁标记为null,修改state,返回true
if(c != 0)	//锁未释放完毕
	//修改state,返回false

这就是ReentrantLock的unLock()方法源码的实现。尝试解锁的过程为:

  1. 看锁是否是被当前线程占有,若不是当前线程占有,则会报IllegalMonitorStateException这个错误
  2. 若是当前线程占有,则通过c = state - release是否为0判断锁是否释放完毕
  3. 若c为0,锁释放完毕,修改exclusiveOwnerThread为null,返回true
  4. 若c不为0,说明锁未释放完毕,修改state,返回false

然而ReentrantLock中lock()方法 调用的是acquire()方法,在ReentrantLock中没看到此方法,因为这个方法是AQS实现的,先看看源码:

 public final void acquire(int arg) {
        if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
            selfInterrupt();
        }
    }
    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

意思就是

  1. 尝试抢锁
  2. 若抢锁成功,!tryAcquire()为false,不做任何操作
  3. 若抢锁失败,!tryAcquire()为true,再判断当前线程在等待队列中是否是队列头部,若是队列头部则继续抢锁,若不是队列头部,则将此线程加入等待队列,并将线程挂起。
公平锁

最后再来看看ReentrantLock的公平锁实现方式,公平锁通过在构造方法中传入true来使sync引用指向公平锁的对象,公平锁这个内部类继承自Sync,重写了其tryAcquire()方法。看看源码。

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

        FairSync() {
        }

        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = this.getState();
            if (c == 0) {
            	//加入对队列头部的判断
                if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, acquires)) {
                    this.setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == this.getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(nextc);
                return true;
            }

            return false;
        }
    }

这个tryAcquire()方法与非公平锁中的方法基本一致,区别在于,不在等待队列中的线程无法直接抢锁成功,需先进入队列中等待其成为队列头部才可以抢到锁。其他方法与非公平锁一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值