AQS组件之ReentrantLock源码分析

ReentrantLock介绍

我们知道ReentrantLock是juc包下的一个锁,它主要用lock方法来加锁,保证同步块同一时间只能有一个线程运行,但有比synchronized 更加丰富的功能,比如:公平和非公平(synchronized只有非公平锁),可中断响应(当获取到的锁被中断时会抛出中断异常,同时释放锁),超时等待(超过一定的时间未获得锁便不再等待)等

ReentrantLock 通过方法lock()与unlock()来进行加锁与解锁操作,与synchronized 会被JVM 自动解锁机制不同,ReentrantLock 加锁后需要手动进行解锁。为了避免程序出现异常而无法正常解锁的情况,使用ReentrantLock 必须在finally 控制块中进行解锁操作。如果通过创建Condition 对象来使线程wait,必须先执行lock.lock 方法获得锁,
方法condition.signal()相当于notify(),用于唤醒正在wait的线程

关于ReentrantLock和synchronized的性能比较:
在竞争不激烈的情况下synchronized优于ReentrantLock,因为synchronized内部对锁进行了优化,由偏向锁-轻量级锁-重量级锁对锁进行了升级,而偏向锁内部不用cas机制获得锁,不需要cas来加锁和解锁的开销,偏向锁会在markword上写入偏向的线程id,如果一直是这一个线程进入同步块则无需进行其他加锁解锁操作,偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线程得到了偏护。
但在竞争激烈的情况下,ReentrantLock的 性能优于synchronized,因为synchronized的重量级锁涉及操作系统的状态切换(用户态-内核态)非常消耗时间,因此重量级锁效率很低,而ReentrantLock采用cas是一种“无锁”思想,也即是乐观锁,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败,具体操作使用到了 unsafe 类,底层用到了本地方法 unsafe.compareAndSwapInt 比较交换方法。这种操作是 CPU 指令集操作,只有一步原子操作,速度非常快。而且 CAS 避免了请求操作系统来裁定锁问题。

源码分析

先介绍一些aqs的知识:
AQS内部用一个volatile修饰的int类型的成员变量state来控制同步状态。

  • state = 0:表示没有线程正在独占共享资源的锁。
  • state = 1:表示有线程正在共享资源的锁。

AQS定义两种资源共享方式:

Exclusive-独占,只有一个线程能执行,如ReentrantLock

Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch 。
AQS定义两种队列
注意:通过Node我们可以实现两个队列,⼀是通过prev和next实现CLH队列(线程同步队列,双向队列),⼆是nextWaiter实现Condition条件上的等待线程队列(单向队列),这个Condition主要⽤在ReentrantLock类中。

Condition接口的主要实现类是AQS的内部类ConditionObject每个Condition对象都包含一个等待队列。该队列是Condition对象实现等待/通知的关键。

AQS使用了模板方法模式,自定义同步器时需要重写下面几个AQS提供的模板方法:
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

AQS类中的其他方法都是final ,所以无法被其他类使用,只有这几个方法可以被其他类使用。

ReentrantLock的加锁方法

首先看它的lock方法和Trylock方法:

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
     public void lock() {
        sync.lock();
    }
        public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);//可中断式获取锁
    }
        public boolean tryLock() {
        //仅当调用时没有被另一个线程持有时才获取锁。如果锁没有被另一个线程持有,则获取锁并立即返回值为 true,将锁持有计数设置为 1。即使此锁已设置为使用公平排序策略,调用 tryLock() 也会立即获取该锁(如果可用),无论其他线程当前是否正在等待该锁。
        return sync.nonfairTryAcquire(1);
    }

    ...}

trylock和lock:
1.tryLock不管拿到拿不到都直接返回;lock如果拿不到则会一直等待。
2.tryLock是可以中断的
它内部实际上是调用了sync.lock和 nonfairTryAcquire方法,
sync:这是一个抽象类, 继承了aqs:

  abstract static class Sync extends AbstractQueuedSynchronizer {//继承了aqs抽象队列同步器
         abstract void lock();//有公平实现和非公平实现
            final boolean nonfairTryAcquire(int acquires) {//非公平获得锁
               
  //首先查看这个对象是否已经被加锁,如果未被加锁则用cas方法试图获得锁,如果获得锁成功则将获得这个锁的线程设置为自己,如果已经被加锁则判断是否是自己加的锁 如果是自己加锁则加重入锁并修改state值,否则返回false
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);//cas方法尝试以独占的方式获取锁
                    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;
        }
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {//返回获得锁的线程,如果没有线程获得锁,返回null
            return getState() == 0 ? null : getExclusiveOwnerThread();//获得当前拥有独占访问权限的线程。
        }

        final int getHoldCount() {//返回获得锁的数量
            return isHeldExclusively() ? getState() : 0;
        }
    }

我们来看超时获取锁的方法:

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }//尝试以独占模式获取,如果被中断则中止,如果超过给定的超时时间则失败。首先检查中断状态,然后调用至少一次tryAcquire,成功返回。否则,线程排队,可能重复阻塞和解除阻塞,调用 tryAcquire 直到成功或线程被中断或超时过去。

关于获取公平锁的方法主要是多了一个!hasQueuedPredecessors() 判断:

   if (c == 0) {
                if (!hasQueuedPredecessors() &&
  compareAndSetState(0, acquires)) {
  setExclusiveOwnerThread(current);
  return true;
  //hasQueuedPredecessors()用于查询是否有任何线程等待获取的时间比当前线程长。
                }
            }

ReentrantLock的condition

再看ReentrantLock内部的Condition:
Condition 将对象监视器方法(wait、notify 和 notifyAll)分解为不同的对象,通过将它们与任意 Lock 实现的使用结合起来,使每个对象具有多个等待集的效果。 Lock 代替了同步方法和语句的使用,Condition 代替了 Object 监视器方法的使用

public Condition newCondition() {
    return sync.newCondition();
}
//      sync.newCondition():
final ConditionObject newCondition() {
            return new ConditionObject();
        }

ConditionObject:是aqs类下面的一个类:

    public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
        private Node addConditionWaiter() {
        //加入一个结点到等待队列中
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;//如果队列为空则将首结点设置为该新结点
            else
                t.nextWaiter = node;//否则插入到尾结点后面
            lastWaiter = node;//设置新的尾结点,类似尾插法
            return node;
        }

await方法:
使用了 LockSupport.park(this)用来挂起线程
LockSupport:用于创建锁和其他同步类的基本线程阻塞原语。

  public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
            //sOnSyncQueue:如果一个节点(始终是最初放置在条件队列中的节点)现在正在等待重新获取同步队列,则返回 true
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

今天的源码分析就到这啦

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值