ReentranLock.lock()实现原理

1. 实现流程

ReentrantLock.lock 锁机制内部是委托java.util.concurrent.locks.ReentrantLock.Sync.lock()实现的。java.util.concurrent.locks.ReentrantLock.Sync是抽象类,有java.util.concurrent.locks.ReentrantLock.FairSync和java.util.concurrent.locks.ReentrantLock.NonfairSync两个实现,也就是常说的公平锁和不公平锁。

ReentrantLock通过持有抽象静态内部类Sync继承AQS,并声明抽象方法lock(),其具体实现在FairSync和NonfairSync中

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

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

在FairSync中lock()实现如下:

//java.util.concurrent.locks.ReentrantLock.Sync实现:
 final void lock() {
            acquire(1);//调用AQS的acquire()
        }

在NonfairSync中lock()实现如下:

//java.util.concurrent.locks.ReentrantLock.NonfairSync实现:
 final void lock() {
 //非公平锁比公平锁性能更好,挂起的线程重新开始与它真正开始运行,二者之间会产生严重的延时
 //如果当前资源没被上锁,则直接请求锁,不用进入队列排队
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

所以,ReentranLock.lock()本质上还是通过调用AQS中acquire(int arg)来实现。

2. AQS中acquire(int arg)的详细步骤解读:

//java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(int arg)实现:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt(); //清中断标志位
    }
    //1、如果tryAcquire(arg)成功,那就没有问题,已经拿到锁,整个lock()过程就结束了。如果失败进行操作2。
    //2、调用addWaiter方法:将当前线程创建一个独占节点(Node)并且此节点加入CHL队列末尾。进行操作3。
    //3、自旋尝试获取锁,失败根据前一个节点来决定是否挂起(park()),直到成功获取到锁。进行操作4。
    //4、如果当前线程已经中断过,那么就中断当前线程(清除中断位)。

⑴. tryAcquire()实现如下:

NonfairSync:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();//对于AQS存在一个state来描述当前有多少线程持有锁
             /*
                如果c等于零,则没有线程持有锁,则将锁给当前线程即可
                如果c不等于,就先判断此线程有没有拥有锁,
            */
            if (c == 0) {
            //compareAndSetState调用compareAndSwapInt设置state状态
                if (compareAndSetState(0, acquires)) {
                //设置当前线程独占访问;exclusiveOwnerThread记录当前持有独占锁的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //ReenTrantLock是可重入锁
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//自己持有锁,无需CAS
                return true;
            }
            return false;
        }

FairSync:

         //与非公平锁实现原理差不多,唯一区别是获取锁的前提是需要确保当前线程在队首
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //区别①:多了一个hasQueuedPredecessors()判断当前线程是否在队首
                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;
        }

⑵. addWaiter实现如下:

tryAcquire失败时需要进入队列:

//java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter

//AQS中Node节点用来保存获取同步状态失败的线程引用,等待状态以及前驱和后继节点;Node节点以双向同步队列形式存储
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //优先尝试快速方式入队
        Node pred = tail; //当前节点指向队尾
        if (pred != null) { //如果队列不为空
            node.prev = pred; 
            //compareAndSetTail:CAS设置尾节点,pred-当前认为的尾节点,node-设置node为尾节点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //同步队列中没有节点,则调用enq方法
        enq(node); 
        return node;
    }
 /**
   * 状态字段,仅接受值:
   * 
   * SIGNAL:值为-1 ,后继节点的线程处于等待状态,
   * 而当前节点的线程如果释放了同步状态或者被取消,
   * 将会通知后继节点,使后继节点的线程得以运行。
   *     
   * CANCELLED:值为1,由于在同步队列中等待的
   * 线程等待超时或者被中断,需要从同步队列中取消等待,
   * 节点进入该状态将不会变化
   *
   * CONDITION: 值为-2,节点在等待队列中,
   * 节点线程等待在Condition上,当其他线程
   * 对Condition调用了singal方法后,该节点
   * 将会从等待队列中转移到同步队列中,加入到
   * 对同步状态的获取中
   *
   * PROPAGATE: 值为-3,表示下一次共享模式同步
   * 状态获取将会无条件地传播下去
   * 
   * INITIAL: 初始状态值为0 
   */
/*
1(CANCELLED):表示当前节点被取消,通过lock()加锁的节点是不会处于这种状态的,只有通过lockInterruptibly()方法加锁才会有这种状态,因为通过lock()加锁的时候,线程是不会响应中断的。
0:表示节点刚被创建,是初始状态。
-1(SIGNAL):表示一个节点的后继正在被阻塞,所以这个节点释放锁或者被中断后,必须unpark它的后继节点。为了避免锁的竞争,acquire()方法必须先保证节点处于SIGNAL状态,然后再去自动的获取锁,如果失败就阻塞。
-2(CONDITION):这个是Condition所使用的状态,,AQS所维护的队列不会处于这个状态,只有在AQS的内部类ConditionObject所维护的一个队列的节点才会处于这个状态。
-3(PROPAGATE):共享模式下才会用到这个状态。
*/

⑶. acquireQueued()实现如下:

调用acquireQueued(Node node,int arg)方法,使得节点以“死循环”的方式获取同步状态(自旋锁)。如果获取不到则阻塞节点中的线程,而被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。节点进入到同步队列后,进入了一个自旋的过程,每个节点都在自省的观察,当条件满足,获取到了同步状态时,就可以从这个自旋中退出,否则继续自旋并且阻塞节点的线程

//java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋锁
            for (;;) {
                //返回上一个节点;如果为Null,抛出NullPointerException 
                final Node p = node.predecessor(); 
                if (p == head && tryAcquire(arg)) {//成功获取同步状态
                    setHead(node);//将节点设置为头节点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //检测当前节点是否应该park(),如果应该park()就挂起当前线程。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //如果park()过程发生中断,则中断标志位为true
                    interrupted = true;
            }
        } finally {
            if (failed)//如果需要中断,则把节点从队列中移除
                cancelAcquire(node);
        }
    }

//java.util.concurrent.locks.AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire如下:
  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        //如果前一个节点的等待状态waitStatus==SIGNAL(SIGNAL的含义为表示当前节点的后继节点包含的线程需要运行)
        //即也就是前面的节点还没有获得到锁,那么返回true,表示当前节点(线程)就应该park()了。
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
            //递归找到一个前驱状态不为CANCELLED的节点作为前驱
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * 前一个节点等待状态waitStatus=0 或者是PROPAGATE,
             * 修改前一个节点状态位为SINGAL,表示后面有节点等待你处理,
             * 需要根据它的等待状态来决定是否该park()
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

3. 参考资料:

  1. https://blog.youkuaiyun.com/u010412719/article/details/52083731
  2. https://blog.youkuaiyun.com/fjse51/article/details/54694714
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值