从零开始java多线程并发---锁(六):AQS的独占锁功能的实现(从Lock以及实现ReentraintLock分析)

一:分析独占锁

  JUC对于资源的共享资源的分类分为2种:共享不可改变资源(fianl修饰)和共享可变资源。因此产生了JUC中的2种锁类型:共享锁和独占锁。

  上一节我们简述了Lock接口以及其实现类ReentraintLock的源码以及常见用法。该Lock是一个独占锁的API,下面我们就给予该Lock接口做一些源码分析。

  关于一些常用的说明
同步状态state:

                           

线程被封装成Node节点的状态:

                        

二:ReentrantLock源码分析独占锁(公平锁)

(1)ThreadA获取锁(lock())(第一个线程)

       说明:由于A线程为第一个线程,所以返回结构为True,并且AQS的FIFO队列为NUll

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

        final void lock() {
            acquire(1);
        }

        /**
         * 尝试获取锁
         * 
         */
        protected final boolean tryAcquire(int acquires) {

            //设置线程为当前线程
            final Thread current = Thread.currentThread(); 

            //获取到同步状态
            int c = getState();

            //c=0表示锁未被占用
            if (c == 0) {

                //当前线程没有其他线程,CAS更新同步状态
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {

                    //设置为独占线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断该线程是否重入
            else if (current == getExclusiveOwnerThread()) {

                 //重入次数+1
                int nextc = c + acquires;
                if (nextc < 0)
                    //重入达到最大次数
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

 

(2)ThreadB尝试获取锁

   由于锁被ThreadA占用,所以上面的tryAcquire()返回false。然后将Thread封装成一个节点。ThreadB获取锁有4个过程

  • 获取当前锁失败(锁被A占用)
  • 封装成Node节点插入FIFO队列
  • 判断能否被唤醒
  • 判断是否阻塞       

/**
*将Node节点添加到AQS的FIFO队列中
*/
 private Node addWaiter(Node mode) {      
        Node node = new Node(Thread.currentThread(), mode);

       //获取到前一个节点
        Node pred = tail;
        
        //前一个节点存在(在介绍FIFO队列时,提到一个东西:在FIFO队列中前后节点用“属性”关联,方便
         处理中断和超时)
        if (pred != null) {
            //当前当前节点的“前一个属性”为前一个节点
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                //前一个节点的“后一个节点属性”为当前节点
                pred.next = node;
                return node;
            }
        }
        
        //前一个节点不存在,将当前节点插入到队列中
        enq(node);
        return node;
    }

 

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
              //当前节点可以被前一个节点唤醒
            return true;
        if (ws > 0) {
               //当前节点抛出异常,移除 
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
                
            //初始化节点状态0
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

(3)ThreadA释放锁(unlock())

  ThreadA执行完毕,释放锁经历一下过程:

  •  尝试释放锁
  • 释放成功唤醒FIFO队列中的首节点进行执行

 

        

 

/**
*尝试释放锁
*/
 protected final boolean tryRelease(int releases) {
            //同步状态 -1 
            int c = getState() - releases;
            
            //释放锁和持有锁的线程不是一个抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            
            boolean free = false;

            //所释放成功
            if (c == 0) {
                free = true;
                //清除占有线程
                setExclusiveOwnerThread(null);
            }

            //充值同步状态
            setState(c);
            return free;
        }

(4)ThreadB被唤醒 

  ThreadB被唤醒,开始进行执行获取锁操作

  • 判断Thread是否中断
  • ThreadB开始获自旋换取到锁(就同之前的一样了了)

               

三:ReentrantLock源码分析独占锁(非公平锁) 

 

四:分析 

  至此,以Lock分析锁独占功能就到这里了,实则独占功能就是依靠FIFO队列的CAS操作保证了队列的有序性和同步性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值