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

    ReentrantLock类注释

    1、可重入互斥锁,意思是表示该锁能够支持一个线程对资源的重复加锁,该锁还支持获取锁的公平和非公平性选择。synchronized关键字隐式的支持重进入。
    2、可以通过isHeldByCurrentThread判断当前线程是否拿到锁。
    3、构造器接收fairness参数,fairness=true表示公平锁反之是非公平锁。
    4、tryLock() 无参方法没有遵循公平性,是非公平的。
    5、公平锁的吞吐量没有非公平锁高,需要不断的进行线程之间的切换

    ReentrantLock类结构

    public class ReentrantLock implements Lock, java.io.Serializable 
    

    在这里插入图片描述
    Lock所定义的方法。
    在这里插入图片描述

    • lock():获取锁,调用该方法的线程会获取锁。
    • unlock(): 释放锁
    • tryLock(): 尝试非阻塞的获取锁,获取成功返回true,失败返回false。
    • tryLock(long,TimeUtil): 超时获取锁,有三种情况:当前线程在超时时间内获得了锁,当前线程在超时时间内被中断,超时时间结束,返回false。
    • lockInterruptedException(): 可中断的获取锁,所在获取过程中可以中断当前线程。

    公平锁与非公平锁

    简言之,如果在绝对的时间上,先对锁发起请求的那个线程会先获取锁,遵循FIFO原则,也就是等待时间最长的那个线程最先获取锁。

    公平锁和非公平锁只是相对于获取锁的时候而言。

    如果一个锁是公平的,那么锁获取的顺序符合时间顺序。当一个线程获取了同步状态就可以认为获取到了锁,刚释放锁的线程下次再获取锁的概率非常大,使其他线程在同步队列中等待,因此非公平锁的线程切换次数很少,这是相对公平锁而言,所以吞吐量上,非公平锁更有优势,所以默认情况下,ReentrantLock的构造就是非公平锁。

    互斥锁

    互斥锁:同一时刻下,只有一个线程可以拿到锁,其他线程只能在同步队列中等待,只有拿到锁的线程释放锁,其他线程才能去争抢;

    构造方法

    
        public ReentrantLock() {
        // 默认是非公平锁
            sync = new NonfairSync();
        }
    
      
        public ReentrantLock(boolean fair) {
        
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    从构造器中可以看出,公平锁是依靠 FairSync 实现的,非公平锁是依靠 NonfairSync 实现的。

    Sync同步器

    Sync继承了AQS,lock方法留给FairSync和NonFairSync两个子类实现,他实现了AQS的tryRelease方法和isHeldExclusively方法。

    abstract static class Sync extends AbstractQueuedSynchronizer{ 
            // 尝试释放锁
            protected final boolean tryRelease(int releases) {
                // releases一般是1,当前线程的同步状态-同步值
                int c = getState() - releases;
                // 当前线程没有持有锁,抛异常 
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                // 当前线程持有的锁都释放了
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                // c!=0代表是重入锁,锁没有释放完毕,前面已经用state-releases
                setState(c);
                return free; // return false,尝试释放锁失败
            }
    
            protected final boolean isHeldExclusively() {
                // While we must in general read state before owner,
                // we don't need to do so to check if current thread is owner
                return getExclusiveOwnerThread() == Thread.currentThread();
            }
        }
    

    加锁

    先来看FairSync公平锁的lock方法和tryAcquire

      static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    		// 尝试加锁,acquire方法是AQS的获取锁的方法,如果尝试获取锁失败,会进入同步队列中等待
            final void lock() {
                acquire(1);
            }
            /**
            AQS的acquire
            public final void acquire(int arg) {
    	        if (!tryAcquire(arg) &&
    	            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    	            selfInterrupt();
        	}
            **/
    
            /**
             * Fair version of tryAcquire.  Don't grant access unless
             * recursive call or no waiters or is first.
             */
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                 // hasQueuedPredecessors 是实现公平的关键
            // 会判断当前线程是不是属于同步队列的头节点的下一个节点(头节点是释放锁的节点)
            // 如果是(返回false),符合先进先出的原则,可以获得锁
            // 如果不是(返回true),则继续等待
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                // 如果获取锁的线程再次请求锁,将通天阁不状态值增加并返回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;
            }
        }
    

    再来看NonFairSync非公平锁的加锁和尝试获取锁

     static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
            final void lock() {
            // 通过判断当前线程是否获取锁的线程来决定获取操作是否成功,如果同步状态值成功修改,那就代表当前线程获取到了锁
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    		// 调用的是父类Sync的尝试获取锁的方法
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
    

    释放锁

    unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:

    1. 尝试释放锁,如果释放失败,直接返回 false;
    2. 释放成功,从同步队列的头节点的下一个节点开始唤醒,让其去竞争锁。

    第一步就是我们上文中 Sync 的 tryRelease 方法(4.1),第二步 AQS 已经实现了。
    释放锁通过LockSupport.unpark(s.thread);

    unLock 的源码如下:

    // 释放锁
    public void unlock() {
        sync.release(1);
    }
    
  • 相关阅读:
    Windows如何自定义U盘盘符、文件夹图标、文件夹背景
    BIN文件如何打开
    Windows COM Surrogate 已停止工作怎么办
    EasyBoot使用方法
    SRS Audio Sandbox没有声音怎么办
    [Compose] 20. Principled type conversions with Natural Transformations
    [Compose] 19. Leapfrogging types with Traversable
    [Compose] 17. List comprehensions with Applicative Functors
    [Compose] 15. Applicative Functors for multiple arguments
    [Algorithm] Coding Interview Question and Answer: Longest Consecutive Characters
  • 原文地址:https://www.cnblogs.com/itjiangpo/p/14181320.html
Copyright © 2011-2022 走看看