ReenTrantLock源码分析(JDK1.8)

1、重入锁ReentrantLock简介

  1. 该锁支持一个线程对资源的重复加锁,注意不是支持多个线程同时获取资源
  2. 获取锁时,支持公平性 和 非公平性
  3. synchronized 隐式的支持重进入
  4. 注意,这是一个独占式的获取

2、公平性与非公平性的比较

  1. 公平锁的效率没有 非公平高

  2. 公平锁能够减少饥饿

  3. 默认是非公平,可以从源码中看出来

  4. 非公平锁:不能保证正在排队的线程能拿到锁,因为可能被新来的线程抢走

3、继承关系图

4、实现重进入

  1. 线程再次获取锁:需要判断当前要获取锁的线程是否为当前占有锁的线程
  2. 锁的最终释放:线程n次获取锁,则需要释放n次,其他线程才能再获取锁

5、源码分析

ReentrantLock是在AQS的基础上实现的。其内部有3个内部类:Sync、NonfairSync 、 FairSync

5.1 、Sync

Sync继承AQS,是我们自定义的同步器,NonfairSync 、 FairSync都是在继承Sync的基础上发展来的。Sync中的方法默认是非公平的。Sync主要实现了方法 tnonfairTryAcquire(int)   tryRelease(int)  等。注意公平、非公平只是获取不同,释放都是一样的。定义了抽象方法lock(int),没有定义unlock(int)方法,这个方法在ReentrantLock()中定义。

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

    abstract void lock();  //具体实现在其子类中

    //重点1(获取资源)
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();  //当前线程
        int c = getState(); //获取当前剩余资源数
        if (c == 0) {  //当前没有线程占有资源
            //尝试CAS设置status,因为在if(c==0)到CAS设置之间可能有其他线程插队,因此有失败的可能性
            if (compareAndSetState(0, acquires)) {  
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {  //如果当前线程是占有线程
            int nextc = c + acquires;  //先计算出预期的status,注意此时并没有设置status
            if (nextc < 0) // 参数异常
                throw new Error("Maximum lock count exceeded");
            setState(nextc);  //设置status,不用考虑多西安城竞争(以为只有本线程才可以到这里)
            return true;
        }
        return false;
    }

    //重点2(释放资源,不一定是完全释放)
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;  //计算预期status
        //如果当前线程并不是占有线程,则抛出异常
        if (Thread.currentThread() != getExclusiveOwnerThread()) 
            throw new IllegalMonitorStateException();
        boolean free = false;  //完全释放标志
        if (c == 0) {  //status==0,表示完全释放
            free = true;
            setExclusiveOwnerThread(null);  //设置占有线程为空
        }
        setState(c);  //设置status(不用考虑多线程竞争,因为只有持有资源的线程1个,才可以到这里)
        return free;
    }

    //是否被当前线程占有
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    //获取当前占有资源的线程
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    //获取占有次数
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
        return getState() != 0;
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

我们重点应该关注在  获取资源 | 释放资源的两个方法,特点如下:

  1. Sync实现的是 非公平(NonfairSync)的方法。因此nonfair中只tryAcquire(int)只是简单的调用Sync中的放啊;而fair中却要自己另外实现tryAcquire(int)方法
  2. lock() 在Sync中式抽象方法,具体实现在其子类中
  3. 释放资源时,先判断当前线程是否是占有线程。如果不是,则抛出异常;如果是,则在设置status时,不同考虑多线程竞争问题,因为支持持有资源的线程才可以设置status
  4. 在获取资源时,要先判statuc==0?。如果当前没有线程占有,则要CAS设置status,因为多线程竞争。如果有线程占有,要分下面两种情况:时当前线程占有,则直接设置status,不用CAS;如果不是当前线程占有,抛出异常
  5. 在初次占有时,和完全释放时,注意要设置占有线程

5.2、 NonfairSync

NonfairSync继承自Sync,并且Sync默认是实现的nonfair,因此tryAcquire(int)只是简单的调用了nonfairTryAcquire(int),而tryRelease(int)则是从Sync中继承来的。lock()需要自己实现。没有unlock(int)方法。

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

    //获取锁
    final void lock() {
        if (compareAndSetState(0, 1))  //先尝试CAS设置0-1,如果设置成功,则设置占有线程
            setExclusiveOwnerThread(Thread.currentThread());
        else  //CAS设置0-1失败,执行Sync中的acquire(1),而Sync中acquire又是调用tryAcquire(int)方法
            acquire(1);
    }

    //获取资源只是简单的调用Sync中的nonfairTryAcquire
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

可以看出NonFairSync中的lock(int)方法就是调用Sync(AQS)中的acquire(int)方法,而acquire(int)方法调用tryAcquire(int)方法,tryAcquire(int)调用 nonfairTryAcquire(int) 方法。

5.3、FairSync

FairSync中的tryAcquire(int)方法是自己实现的,和Sync中的 nonfairTryAcquire(int) 无关。lock(int) 放啊也是直接调用的自己(从AQS继承传递)的 acquire(int) 方法。没有unlock(int) 方法。

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

    //获取锁,直接调用AQS继承来的acquire(int)方法,acquire(int)中调用tryAcquire(int)
    final void lock() {
        acquire(1);
    }

    //实现tryAcquire(int)获取共享资源
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread(); //当前线程
        int c = getState();  //当前status
        if (c == 0) {  //如果还没有线程占有
            //注意,这里是体现fair的地方(先判断当前节点有没有前驱节点),排队越久,越优先
            if (!hasQueuedPredecessors() &&   
                compareAndSetState(0, acquires)) {  //CAS设置status,因为此处有竞争
                setExclusiveOwnerThread(current);  //如果CAS成功,则设置线程(注意,如果能到这里,肯定只有CAS成功的线程一个,不存在竞争)
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {  //当前线程是占有线
            int nextc = c + acquires;  //计算预期status
            if (nextc < 0)  //参数异常
                throw new Error("Maximum lock count exceeded");
            setState(nextc);  //设置status,这里不存在竞争
            return true;
        }
        return false;
    }
}
//判断当前线程有没有前驱节点,有则返回true
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());  
}

总结:

  1. 不同于NonFair的tryAcquire(int)直接调用Sync的nonFairTryAcquire(int)。FairSync需要自己写tryAcquire(int)方法。与NonFair不同的地方是,加入了 hasQueuedPredecessors() 判断当前线程有没有前驱节点。如果没有前驱节点,那么它可以去竞争资源。
  2. lock(int)方法和非公平一样,只是简单的调用 acquire(int)

5.4、ReentrantLock中的成员变量和构造方法

private static final long serialVersionUID = 7373984872572414699L; //序列化

private final Sync sync;  //内部的同步器,这只是个引用,具体实例肯定是 FairSync或者NonFairSync

//构造方法

//1.默认,非公平
public ReentrantLock() {
    sync = new NonfairSync();
}

//2.根据参数boolean来构造内部同步器类型
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

总结:

  1. ReentrantLock的成员变量非常简单,就是有一个序列化参数  和 Sync自定义同步器
  2. ReentrantLock的公平性实现方式:Sync的具体实现,根据构造方法的参数来实例化 FairSync或者NonFairSync实例

5.5、ReentrantLock的各种获取锁(正常、响应中断、超时等)

public void lock() {  //不响应中断
     sync.lock();  //就是简单的调用Sync子类的lock方法,实质上是调用AQS的acquire(int)
}

 

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);  //调用AQS的acquireInterruptibly(1)
}

//注意acquireInterruptibly(1)中也是调用了tryAcquire(int)方法,而tryAcquire(int)方法在公平锁和非公平锁的实现不同。

 注意:lockInterruptibly()和其他所有的lock()方法最后在底层都调用了tryAcquire()。因此任何lock方法都区分公平 与 非公平。

 

//非阻塞的获取锁(非公平)
public boolean tryLock() {
    return sync.nonfairTryAcquire(1); //少了acquire中的acquireQueud()阻塞
}
//超时获取锁(响应中断)
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

总结:除了tryLock(int)是直接调用的Sync.nonfairTryAcquire(1),其他的获取锁都是调用的AQS中对应的方法

5.6、释放锁

public void unlock() {
    sync.release(1);  //不管公不公平,都是调用Sync中的release(int),也是Sync直接从AQS中继承来的
}

总结:直接调用的AQS中的release(1),Sync并没有改写release()

5.7、ReentrantLock中的其他方法(不一一列举,之说自己感兴趣的)

//判断公平性
public final boolean isFair() {
    return sync instanceof FairSync;
}

6、总结

由上面的分析,可以得到下面的结论

比较底层的同步队列进队、出队问题,已经在AQS解决了。而实现ReentrantLock时,主要时实现其内部自定义同步器Sync。但ReentrantLock分为两种模式:公平、非公平;这两种模式的实现是在 FairSync 和 NonFairSync 继承Sync的基础上,对 tryAcquire(int)和lock(int)的具体实现不同来实现的。而 unlock() 不区分公平性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值