ReentrantLock源码,CAS,AQS,CountDownLatch

CAS

Compare And Swap (Compare And Exchange) / 自旋 / 自旋锁 / 无锁 (无重量锁)

因为经常配合循环操作,直到完成为止,所以泛指一类操作

cas(v, a, b) ,变量v,期待值a, 修改值b

ABA问题,你的女朋友在离开你的这段儿时间经历了别的人,自旋就是你空转等待,一直等到她接纳你为止

解决办法(版本号 AtomicStampedReference),基础类型简单值不需要版本号

CAS典型应用
java.util.concurrent.atomic 包下的类大多是使用CAS操作来实现的(eg. AtomicInteger.java,AtomicBoolean,AtomicLong)。下面以 AtomicInteger.java 的部分实现来大致讲解下这些原子类的实现。
 

AQS:

AQS(AbstractQueuedSynchronizer),AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架。这个抽象类被设计为作为一些可用原子int值来表示状态的同步器的基类。如果你有看过类似 CountDownLatch 类的源码实现,会发现其内部有一个继承了 AbstractQueuedSynchronizer 的内部类 Sync 。可见 CountDownLatch 是基于AQS框架来实现的一个同步器.类似的同步器在JUC下还有不少。(eg. Semaphore )
在这里插入图片描述

AQS 原理概览

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

AQS(AbstractQueuedSynchronizer)原理图:
在这里插入图片描述

AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改

    /**
     * The synchronization state.
     *//共享变量,使用volatile修饰保证线程可见性
     */
    private volatile int state;

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    
    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     * 使用CAS底层底层实现进行修改,底层为本地native方法,是c++书写的,
     * 底层c++ LOCK_IF_MP(mp)  指令:lock cmpxchg 指令 
     * lock指令在执行后面指令的时候锁定一个北桥信号,实现原子性
     *  多线程情况下,与CPU交互的底层指令会加lock锁,
    
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

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

Exclusive(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
公平锁:按照线程在队列中的排队顺序,先到者先拿到锁  //lock();方法
非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的 //tryLock()方法
Share(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock。
ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。

AQS底层使用了模板方法模式
同步器的设计是基于模板方法模式的,如果需要自定义同步器一般的方式是这样:

使用者继承AbstractQueuedSynchronizer并重写指定的方法。(这些重写方法很简单,无非是对于共享资源state的获取和释放)
将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。

AQS使用了模板方法模式,自定义同步器时需要重写下面几个AQS提供的模板方法:

isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

默认情况下,每个方法都抛出 UnsupportedOperationException。 这些方法的实现必须是内部线程安全的,并且通常应该简短而不是阻塞。AQS类中的其他方法都是final ,所以无法被其他类使用,只有这几个方法可以被其他类使用。

以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS(Compare and Swap)减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

AQS 组件总结
Semaphore(信号量):允许多个线程同时访问, synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。
CountDownLatch (倒计时器): CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。
CyclicBarrier(循环栅栏): CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await()方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
 

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
//tryLock()和try()都是继承内部类Sync,
    private final Sync sync;


    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();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    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;
        }

        protected final boolean tryRelease(int releases) {
            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;
        }

        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();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

而Sync实现AbstractQueuedSynchronizer类

    ReentrantLock如何处理线程中断的?
    AbstractQueuedSynchronizer的parkAndCheckInterrupt()
    阻塞队列使用的LockSupport.park();

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }


因为LockSupport.park(),无法响应Thread.interrupt(); 所以当unpark()后使用Thread.interrupted()来判断线程是否有中断过。如果中断过整个唤醒的线程在外层方法会继续执行一次中断,详情源码如下:

 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                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);
        }
    }


tryLock和lock不同点

1. tryLock不管拿到拿不到都直接返回;lock如果拿不到则会一直等待。

2. tryLock是可以中断的。

锁tryLock();

    ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.tryLock();
    
    public boolean tryLock() {
        //调用SYNC的非公平锁方法,去尝试获得锁
        return sync.nonfairTryAcquire(1);
    }


         /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
//如果当前state为0则表示未被加锁
            if (c == 0) {
//调用CAS方法,将state的状态从未加锁0改为加锁状态1,多线程情况下,可能会出现加锁失败
                if (compareAndSetState(0, acquires)) {  
//加锁成功,则把当前线程设置为,当前独占锁的拥有者
                    setExclusiveOwnerThread(current);
//并返回
                    return true;
                }
            }
//如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        }

 lock()   非公平锁:来了先抢占,抢不到,再进队列

    //默认非公平锁,true表示公平锁,false表示非公平锁,    
    ReentrantLock reentrantLock = new ReentrantLock(false);
        reentrantLock.lock();

    //NonfairSync extends Sync 使用NonfairSync中的lock
        public void lock() {
            sync.lock();
        }
        
        final void lock() {
    //采用cas设置state为1
            if (compareAndSetState(0, 1))
   //设置成功,则表示获得锁,并将当前线程设置为锁的拥有者
                setExclusiveOwnerThread(Thread.currentThread());
            else
    //否则
                acquire(1);
        }
    
    public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
        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();
//如果标志State为0
            if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        
        }
    }


    // 从clh中选一个线程获取占用资源
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    // 如果获取到资源,则将当前节点设置为头节点head
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(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;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }  
    
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { 
                // tail为null,说明还没初始化,此时需进行初始化工作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 否则的话,将当前线程节点作为tail节点加入到CLH中去
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

lock()公平锁下 公平锁:老的线程在排队,新来的线程也一样要排队,不能抢占

  ReentrantLock reentrantLock = new ReentrantLock(true);
        reentrantLock.lock();

    public void lock() {
        sync.lock();
    }

    //FairSync extends Sync,使用FairSync中的lock()方法
        final void lock() {
            acquire(1);
        }

    public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
        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();
//如果标志State为0
            if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        
        }
    }


    // 从clh中选一个线程获取占用资源
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    // 如果获取到资源,则将当前节点设置为头节点head
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(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;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }  
    
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { 
                // tail为null,说明还没初始化,此时需进行初始化工作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 否则的话,将当前线程节点作为tail节点加入到CLH中去
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

 CountDownLatch 类

public class CountDownLatch {
    /**
     * 基于AQS的内部Sync
     * 使用AQS的state来表示计数count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            // 使用AQS的getState()方法设置状态
            setState(count);
        }

        int getCount() {
            // 使用AQS的getState()方法获取状态
            return getState();
        }

        // 覆盖在共享模式下尝试获取锁
        protected int tryAcquireShared(int acquires) {
            // 这里用状态state是否为0来表示是否成功,为0的时候可以获取到返回1,否则不可以返回-1
            return (getState() == 0) ? 1 : -1;
        }

        // 覆盖在共享模式下尝试释放锁
        protected boolean tryReleaseShared(int releases) {
            // 在for循环中Decrement count直至成功;
            // 当状态值即count为0的时候,返回false表示 signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;

    // 使用给定计数值构造CountDownLatch
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    // 让当前线程阻塞直到计数count变为0,或者线程被中断
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    // 阻塞当前线程,除非count变为0或者等待了timeout的时间。当count变为0时,返回true
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    // count递减
    public void countDown() {
        sync.releaseShared(1);
    }

    // 获取当前count值
    public long getCount() {
        return sync.getCount();
    }

    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}
发布了11 篇原创文章 · 获赞 2 · 访问量 140

猜你喜欢

转载自blog.csdn.net/sinat_38195280/article/details/105346499