zoukankan      html  css  js  c++  java
  • ReentrantLock源码了解

        1)、ReentrantLock.tryLock
        //获取没有被其他线程持有的锁
        //1)、当没有被任何线程持有时,首先将计数器设置为1,并设置当前持有锁的线程为当前线程,最后返回true
        //2)、当被当前线程持有时,将计数器加1,最后返回true;
        //3)、否则返回false
        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
    
        //接着直接调用抽象类中的Sync.nonfairTryAcquire
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前调用线程
            final Thread current = Thread.currentThread();
            //获取当前锁的状态,也就是当前锁的计数器的数值
            int c = getState();
            if (c == 0) {
                //此时说明,当前锁没有被任何线程持有
                if (compareAndSetState(0, acquires)) {
                    //使用CAS操作,更新锁的状态,即锁的计数器的数值,若是成功,则将当前锁的线程持有者设置为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                //根据JMM模型可知,同一个工作内存内是可见的,故同一个线程内是可见的,若相等,是同一个线程,且在同一个线程内不会发生setExclusiveOwnerThread时,在getExclusiveOwnerThread得到的不一样
                //此时说明,当前锁的线程持有者是当前线程
                //设置锁的计数器的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //更新锁的状态,即锁的计数值的数值
                setState(nextc);
                return true;
            }
            return false;
        }
    
        2)、ReentrantLock.unlock
        //试图释放锁
        //1)、当前线程不是该锁的持有者时,抛出异常
        //2)、当前锁的计数器更改为0时,则设置当前锁的持有者为null
        //3)、否则,更新当前锁的状态,即锁的计数器的数值
        public void unlock() {
            sync.release(1);
        }
    
        //接着直接调用抽象类AbstractQueuedSynchronizer.release
        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                //当锁的状态为0,或者说当前锁没有持有者时,需要唤醒当前锁上挂起的线程
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    
        //再接着直接调用抽象类Sync.tryRelease
        protected final boolean tryRelease(int releases) {
            //计算当前锁的计数器的数值
            int c = getState() - releases;
            //判断当前线程是否是锁的持有者,若不是,则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                //当计数器的数值为0,说明此时锁没有持有者了,故先更新线程持有者,之后再去更新锁的状态,这样当去获取锁的状态时,此时的锁的持有者必然是更新后的,这样锁的释放和锁的获取就能保证一致的可见性了。
                free = true;
                setExclusiveOwnerThread(null);
            }
            //更新锁的状态,即锁的计数器的数值
            setState(c);
            return free;
        }
    
        //直接调用AbstractQueuedSynchronizer.unparkSuccessor
        //唤醒当前节点后的第一个非取消节点中的线程
        private void unparkSuccessor(Node node) {
            
            //尝试更新当前节点的状态
            int ws = node.waitStatus;
            if (ws < 0)
                compareAndSetWaitStatus(node, ws, 0);
    
            //获取当前节点后的第一个非取消节点,并唤醒该节点中挂起的线程
            Node s = node.next;
            if (s == null || s.waitStatus > 0) {
                s = null;
                //从尾节点开始查找,直到当前节点,即可得到一个当前节点后的第一个非取消节点
                for (Node t = tail; t != null && t != node; t = t.prev)
                    if (t.waitStatus <= 0)
                        s = t;
            }
    
            //唤醒线程
            if (s != null)
                LockSupport.unpark(s.thread);
        }
    
        3)、ReentrantLock.lock
        //申请锁
        public void lock() {
           sync.lock();
        }
    
        31)、非公平锁
        //直接调用NonfairSync.lock
        final void lock() {
            //当通过CAS判断当前锁是否没有持有者,若是,则直接设置当前锁的持有者为当前线程;
            //否则,再次尝试加锁,最后还不成功,就如等待队列,线程挂起,直到被唤醒为止
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
        //直接调用AbstractQueuedSynchronizer.acquire
        public final void acquire(int arg) {
            //尝试获取非公平锁,当失败时,就将当前线程入等待队列,线程挂起,直到被挂起
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
        //直接调用NonfairSync.tryAcquire
           protected final boolean tryAcquire(int acquires) {
               //尝试获取非公平锁
            return nonfairTryAcquire(acquires);
        }
    
        //直接调用AbstractQueuedSynchronizer.addWaiter
        //将排他锁节点添加到链表尾部
        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) {
                //当前链表已经初始化,则CAS尝试将排他锁节点更改为链表的尾节点
    
                //设置当前节点的前一个节点为当前链表的尾节点
                node.prev = pred;
                //当CAS尝试更新尾节点成功,则将新尾节点的前一个节点的下一个节点更新为新的尾节点
                if (compareAndSetTail(pred, node)) {
                    pred.next = node;
                    return node;
                }
            }
            enq(node);
            return node;
        }
    
        //直接调用AbstractQueuedSynchronizer.compareAndSetTail
        //原子更新链表的尾节点
        private final boolean compareAndSetTail(Node expect, Node update) {
            return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
        }
    
        //直接调用AbstractQueuedSynchronizer.enq
        //使用CAS原子性将节点插入到链表尾部
        private Node enq(final Node node) {
            for (;;) {
                Node t = tail;
                if (t == null) { // Must initialize
                    //当没有初始化时,进行头结点与尾节点初始化,成功后,将头结点与尾节点指向同一个处对象
                    if (compareAndSetHead(new Node()))
                        tail = head;
                } else {
                    //将节点插入到链表尾部
    
                    //设置当前节点的前一个节点为当前链表的尾节点
                    node.prev = t;
                    //当CAS尝试更新尾节点成功,则将新尾节点的前一个节点的下一个节点更新为新的尾节点
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }
    
        //直接调用AbstractQueuedSynchronizer.acquireQueued
        //当当前节点为等待队列中的第一个节点时,获取到锁
        final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    //当当前节点的前节点为头结点,并为当前线程尝试获取锁,成功时,设置当前节点为头结点,并将当前节点的前节点的下一个节点取消,使之没有被引用,这样可以被GC回收
                    final Node p = node.predecessor();
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }    
                    //设置当前节点的前节点状态为唤醒状态,且将当前节点挂起
                    if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
        //直接调用AbstractQueuedSynchronizer.setHead
        //设置当前节点为头结点,并将当前节点的前节点置为空,以及将当前节点持有的线程设置为空
        private void setHead(Node node) {
            head = node;
            node.thread = null;
            node.prev = null;
        }
    
        //直接调用AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire
        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 {
                //尝试将当前节点的前节点的状态设置为唤醒状态
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
        }
    
        //直接调用AbstractQueuedSynchronizer.compareAndSetWaitStatus
           //尝试更新节点的状态
        private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) {
            return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
        }
    
        //直接调用AbstractQueuedSynchronizer.parkAndCheckInterrupt
        //将当前线程挂起
        private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);
            return Thread.interrupted();
        }
    
        
    Semaphore:资源控制,是线程同步的工具类,默认是公平策略;如停车场,最多只有100个车位,故当满时,只能出一个之后,才能进一个;
    当新建时,需指定控制的资源的个数,即同时进行的线程数,还有就是使用的是公平还是非公平的策略,默认是非公平;
    acquire获取资源;
    release释放资源;
    
    CountDownLatch:满足条件时触发;如赛跑,在开始前,所有运动员准备好之后,才能开始;当所有运动员到达终点时,才能结束;还如,当获取专辑的所有视频资源信息时,只有所有的视频信息获取到之后,才能进行之后的操作;当新建时,需要指定锁计数的个数;
    
    await:等待锁计数为0,之前一直阻塞
    countDown:递减计数器的计数,如果计数达到0,则释放所有等待的线程;
    
    实际操作都是由其属性Sync来进行,且该属性对象继承了AbstractQueuedSynchronizer类;
    
    参考资料:
    http://www.itzhai.com/the-introduction-and-use-of-a-countdownlatch.html
    http://mouselearnjava.iteye.com/blog/1921468
  • 相关阅读:
    kafka 学习笔记
    awk命令详解
    apache 的 配置项
    Apache 的 httpd.conf 详解
    如何设置httpd-mpm-conf的参数
    apache 服务器概述--安装(一)
    centos 修改时区
    docker(三)docker镜像和镜像发布方法
    docker(二)部署docker容器虚拟化平台
    sql的存储过程使用详解--基本语法
  • 原文地址:https://www.cnblogs.com/xiaoxian1369/p/5899113.html
Copyright © 2011-2022 走看看