精华内容
下载资源
问答
  • Java多线程AQS

    2020-06-25 11:06:49
    队列同步器AbstractQueuedSynchronizer(以下简称同步器或AQS),是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。并发包的大师...

    AbstractQueuedSynchronizer

    学习AQS的必要性

    队列同步器AbstractQueuedSynchronizer(以下简称同步器或AQS),是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。并发包的大师(Doug Lea)期望它能够成为实现大部分同步需求的基础。

    AQS使用方式和其中的设计模式

    AQS的主要使用方式是继承,子类通过继承AQS并实现它的抽象方法来管理同步状态,在AQS里由一个int型的state来代表这个状态,在抽象方法的实现过程中免不了要对同步状态进行更改,这时就需要使用同步器提供的3个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))来进行操作,因为它们能够保证状态的改变是安全的。
    在这里插入图片描述
    在实现上,子类推荐被定义为自定义同步组件的静态内部类,AQS自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用,同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch等)。

    同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器。可以这样理解二者之间的关系:
    锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节;
    同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者所需关注的领域。

    实现者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。

    模板方法模式

    同步器的设计基于模板方法模式。模板方法模式的意图是,定义一个操作中的算法的骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。我们最常见的就是Spring框架里的各种Template。

    实际例子

    我们开了个蛋糕店,蛋糕店不能只卖一种蛋糕呀,于是我们决定先卖奶油蛋糕,芝士蛋糕和慕斯蛋糕。三种蛋糕在制作方式上一样,都包括造型,烘焙和涂抹蛋糕上的东西。所以可以定义一个抽象蛋糕模型
    在这里插入图片描述
    然后就可以批量生产三种蛋糕
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    这样一来,不但可以批量生产三种蛋糕,而且如果日后有扩展,只需要继承抽象蛋糕方法就可以了,十分方便,我们天天生意做得越来越赚钱。突然有一天,我们发现市面有一种最简单的小蛋糕销量很好,这种蛋糕就是简单烘烤成型就可以卖,并不需要涂抹什么食材,由于制作简单销售量大,这个品种也很赚钱,于是我们也想要生产这种蛋糕。但是我们发现了一个问题,抽象蛋糕是定义了抽象的涂抹方法的,也就是说扩展的这种蛋糕是必须要实现涂抹方法,这就很鸡儿蛋疼了。怎么办?我们可以将原来的模板修改为带钩子的模板。
    在这里插入图片描述
    做小蛋糕的时候通过flag来控制是否涂抹,其余已有的蛋糕制作不需要任何修改可以照常进行。
    在这里插入图片描述

    AQS中的方法

    模板方法

    实现自定义同步组件时,将会调用同步器提供的模板方法,
    在这里插入图片描述
    这些模板方法同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放、同步状态和查询同步队列中的等待线程情况。

    可重写的方法

    在这里插入图片描述

    访问或修改同步状态的方法

    重写同步器指定的方法时,需要使用同步器提供的如下3个方法来访问或修改同步状态。
    •getState():获取当前同步状态。
    •setState(int newState):设置当前同步状态。
    •compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性。

    CLH队列锁

    CLH队列锁即Craig, Landin, and Hagersten (CLH) locks。

    CLH队列锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程仅仅在本地变量上自旋,它不断轮询前驱的状态,假设发现前驱释放了锁就结束自旋。

    当一个线程需要获取锁时:
    1.创建一个的QNode,将其中的locked设置为true表示需要获取锁,myPred表示对其前驱结点的引用
    在这里插入图片描述

    2.线程A对tail域调用getAndSet方法,使自己成为队列的尾部,同时获取一个指向其前驱结点的引用myPred
    在这里插入图片描述
    线程B需要获得锁,同样的流程再来一遍
    在这里插入图片描述
    3.线程就在前驱结点的locked字段上旋转,直到前驱结点释放锁(前驱节点的锁值 locked == false)
    4.当一个线程需要释放锁时,将当前结点的locked域设置为false,同时回收前驱结点
    在这里插入图片描述
    如上图所示,前驱结点释放锁,线程A的myPred所指向的前驱结点的locked字段变为false,线程A就可以获取到锁。

    CLH队列锁的优点是空间复杂度低(如果有n个线程,L个锁,每个线程每次只获取一个锁,那么需要的存储空间是O(L+n),n个线程有n个myNode,L个锁有L个tail)。CLH队列锁常用在SMP体系结构下。

    Java中的AQS是CLH队列锁的一种变体实现。

    展开全文
  • } //AQS public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { ... public final void acquireSharedInterruptibly(int arg) throws ...

     

    部分锁释放,部分没有释放,await 会一直等待全部锁释放

        static void work(CountDownLatch lock) {
            System.out.println(Thread.currentThread().getName() + " 释放1把锁");
            lock.countDown();
        }
    
        /**
         * 部分锁释放,部分没有释放,await 会一直等待全部锁释放
         */
        /*
        private final boolean parkAndCheckInterrupt() {
            //Thread.currentThread().getName() 是主线程
            LockSupport.park(this);
            return Thread.interrupted();
        }
         */
        public static void someReleaseTest() {
            //分配3把共享锁
            CountDownLatch lock = new CountDownLatch(3);
    
            new Thread(() -> work(lock)).start();
            new Thread(() -> work(lock)).start();
    
            try {
                lock.await();
                System.out.println("全部锁已释放");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    
    输出:
    Thread-0 释放1把锁
    Thread-1 释放1把锁
    
    一直等待

     

    分配的3把锁全部释放,main 线程继续运行

        /**
         * 分配的3把锁全部释放,main 线程继续运行
         */
        public static void allReleaseTest() {
            //分配3把共享锁
            CountDownLatch lock = new CountDownLatch(3);
    
            new Thread(() -> work(lock)).start();
            new Thread(() -> work(lock)).start();
            new Thread(() -> work(lock)).start();
    
            try {
                //直接返回,不需要part
                lock.await();
                System.out.println("全部锁已释放");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    
    输出:
    Thread-0 释放1把锁
    Thread-1 释放1把锁
    Thread-2 释放1把锁
    全部锁已释放
    

     

    源码解析

    new CountDownLatch(3)

    //构造 CountDownLatch
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    
    //CountDownLatch 的内部类 Sync
    private static final class Sync extends AbstractQueuedSynchronizer {
    
        Sync(int count) {
        	//父类 AQS 的方法
            setState(count);
        }
    ...
    }
    

     

    lock.await();

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    //AQS 
    public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {
        ...
    	public final void acquireSharedInterruptibly(int arg)
    	        throws InterruptedException {
    	    if (Thread.interrupted())
    	        throw new InterruptedException();
    	    if (tryAcquireShared(arg) < 0)
    	        doAcquireSharedInterruptibly(arg);
    	}
    }
    
    //CountDownLatch Sync 实现父类方法
    public class CountDownLatch {
    	...
        private static final class Sync extends AbstractQueuedSynchronizer {
        	...
        	//全部锁释放返回1
            protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
            }
        }	
    	...
    }
    
    
    //如果还有锁没有释放,执行 doAcquireSharedInterruptibly
    public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {
        ...
        private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
    
    		...
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                throw new InterruptedException();    
        }
        ...
    }
    
    
    //真正part线程的方法
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

     

    展开全文
  • ![图片说明](https://img-ask.csdn.net/upload/201908/05/1564936762_969108.jpg) ![图片说明](https://img-ask.csdn.net/upload/201908/05/1564936770_291455.jpg) ![图片说明]...
  • import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class SemaphoreTest { static Semaphore semaphore = new Semaphore(3); static void task(int sec) { try { ...

     

    
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public class SemaphoreTest {
        static Semaphore semaphore = new Semaphore(3);
    
        static void task(int sec) {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " start state=" + semaphore.availablePermits());
                TimeUnit.SECONDS.sleep(sec);
                System.out.println(Thread.currentThread().getName() + " end state=" + semaphore.availablePermits());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    
        /**
         * 信号量,分配n把共享锁
         * 分配3把共享锁,也就设定state的值
         * nonfairTryAcquireShared for 自旋,只有当 state分配完即小于0 或者 CAS更新成功,就返回剩余的state,
         * 如果state小于0 就会执行中断doAcquireSharedInterruptibly(arg) 自旋等候,直到try 获取到共享锁为止
         */
        /*
        //核心方法 Semaphore Sync
        abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = 1192457210091910933L;
    
            Sync(int permits) {
                setState(permits);
            }
    
            final int getPermits() {
                return getState();
            }
    
            final int nonfairTryAcquireShared(int acquires) {
                for (;;) {
                    int available = getState();
                    int remaining = available - acquires;
                    if (remaining < 0 ||
                        compareAndSetState(available, remaining))
                        return remaining;
                }
            }
         */
        public static void main(String[] args) {
            new Thread(() -> task(2)).start();
            new Thread(() -> task(2)).start();
            new Thread(() -> task(6)).start();
    
            new Thread(() -> task(2)).start();
            new Thread(() -> task(2)).start();
        }
    
    }
    
    
    输出:
    Thread-0 start state=2
    Thread-1 start state=1
    Thread-2 start state=0
    Thread-0 end state=0
    Thread-1 end state=0
    Thread-3 start state=1
    Thread-4 start state=0
    Thread-3 end state=0
    Thread-4 end state=0
    Thread-2 end state=2
    

     

    Semaphore 的构造函数 是创建一个非公平锁

    public class Semaphore implements java.io.Serializable {
    ...
        public Semaphore(int permits) {
            sync = new NonfairSync(permits);
        }
    ...
    }

     

    每个线程执行task 都先获取锁 semaphore.acquire

    public class Semaphore implements java.io.Serializable {
    ...
        public void acquire() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }
    ...
    }

     

    Semaphore 的内部类 Sync 继承了 AbstractQueuedSynchronizer。sync.acquireSharedInterruptibly(1); 是调父类AbstractQueuedSynchronizer的acquireSharedInterruptibly方法

    public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
    ...
        public final void acquireSharedInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            if (tryAcquireShared(arg) < 0)
                doAcquireSharedInterruptibly(arg);
        }
    ...
    }

     

    tryAcquireShared(arg) 方法由AQS的子类Sync 的子类NonfairSync 实现

    public class Semaphore implements java.io.Serializable {
    ...
       static final class NonfairSync extends Sync {
            private static final long serialVersionUID = -2694183684443567898L;
    
            NonfairSync(int permits) {
                super(permits);
            }
    
            protected int tryAcquireShared(int acquires) {
                return nonfairTryAcquireShared(acquires);
            }
        }
    ...
    }
    

     

    nonfairTryAcquireShared 由 NonfairSync 的父类Sync实现。

    nonfairTryAcquireShared for 自旋,只有当 state分配完即小于0 或者 CAS更新成功,就返回剩余的state。

    public class Semaphore implements java.io.Serializable {
    
        abstract static class Sync extends AbstractQueuedSynchronizer {
    ...
            final int nonfairTryAcquireShared(int acquires) {
                for (;;) {
                    int available = getState();
                    int remaining = available - acquires;
                    if (remaining < 0 ||
                        compareAndSetState(available, remaining))
                        return remaining;
                }
            }
    ...
    }

     

    如果state小于0 就会执行中断doAcquireSharedInterruptibly(arg) 自旋等候,直到try 获取到共享锁为止。

    public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {
    ...
        private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
            final Node node = addWaiter(Node.SHARED);
            boolean failed = true;
            try {
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head) {
                        int r = tryAcquireShared(arg);
                        if (r >= 0) {
                            setHeadAndPropagate(node, r);
                            p.next = null; // help GC
                            failed = false;
                            return;
                        }
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        throw new InterruptedException();
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    ...
    }

     

     

     

    展开全文
  • 首先,为什么要理解AQS??? 因为同步组件(这里不仅仅指锁,还包括CountDownLatch等)的实现依赖于同步器AQS,即AQS是同步组件实现的核心部分。 那么,AQS到底是什么呢??? AQS(AbstractQueuedSynchronizer),...

    首先,为什么要理解AQS???
    因为同步组件(这里不仅仅指锁,还包括CountDownLatch等)的实现依赖于同步器AQS,即AQS是同步组件实现的核心部分
    那么,AQS到底是什么呢???
    AQS(AbstractQueuedSynchronizer),简称同步器,是用来构建锁和其它同步组件的基础框架。AQS的组成可以理解如下图:
    在这里插入图片描述要想掌握AQS的底层实现,我们就要学习这些模板方法,首先我们就得了解AQS中的同步队列是个什么样的数据结构,因为同步队列是AQS对同步状态管理的基石


    同步队列

    当共享资源被某个线程占有,其他请求该资源的线程将会阻塞,从而进入同步队列。AQS中的同步队列则是通过链式方式进行实现。在AQS有一个静态内部类Node,这是我们同步队列的每个具体节点。在这个类中有如下属性:

    在这里插入图片描述
    现在我们知道了节点的数据结构类型,并且每个节点拥有其前驱和后继节点,很显然这是一个双向队列。AQS实际上通过头尾指针来管理同步队列,同时实现包括获取锁失败的线程进行入队,获取锁成功的线程进行出队,释放锁时对同步队列中的线程进行通知等核心方法。其示意图如下:
    在这里插入图片描述


    AQS的底层实现


    独占锁

    独占锁的获取

    调用lock()方法是获取独占锁,获取失败就将当前线程加入同步队列,成功则线程执行。

    final void lock() {
      if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
      else
        acquire(1);
    }
    `

    使用CAS来尝试将同步状态改为1,若成功则将同步状态持有线程置为当前线程。否则将调用AQS提供的aquire()方法

    public final void acquire(int arg) {
      // 再次尝试获取同步状态,如果成功则方法直接返回
      // 如果失败则先调用addWaiter()方法再调用acquireQueued()方法
      if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
    }

    使用acquire方法再次获取同步状态,若成功则直接返回,若失败就将当前线程加入同步队列,再排队继续获取锁
    上述过程可理解如下图:
    在这里插入图片描述
    接下来我们就分别来研究一下addWaiter()方法和acquireQueued()方法。

    addWaiter()方法——入队
    private Node addWaiter(Node mode) {
      // 将当前线程包装称为Node类型
      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;
       }
     }
      // 当前尾节点为空或CAS尾插失败
      enq(node);
      return node;
    }

    通过上述的代码我们可以发现,addWaiter()方法的流程图如下:
    在这里插入图片描述
    通过上述流程图我们可以发现,enq()方法的执行结果一定是成功的,那么原因是什么呢?我们来分析一下enq()的源码:

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

    通过上述代码我们可以发现,enq()的流程图如下:
    在这里插入图片描述
    现在我们已经很清楚获取独占式锁失败的线程包装成Node然后插入同步队列的过程了,那么我们就要清楚在同步队列中的结点(线程)如何来保证自己能够有机会获得独占式锁了,来分析一下acquireQueued()方法。

    acquireQueued()方法——排队获取锁
    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);
     }
    }

    通过上述代码我们可以发现,acquireQueued()方法的流程图如下:

    在这里插入图片描述
    经过上面的分析,独占式锁的获取过程如下:

    在这里插入图片描述

    独占式锁的释放

    独占式锁的释放调用unlock()方法,而该方法实际调用了AQS的release方法
    unlock()方法:

    public void unlock() {
      sync.release(1);
    }

    release()方法:

    public final boolean release(int arg) {
      if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
          unparkSuccessor(h);
        return true;
     }
      return false;
    }

    通过上述代码我们可以发现,独占式锁的释放过程如下:

    在这里插入图片描述
    独占式锁的总结:
    1. 线程获取锁失败,线程被封装成Node进行入队操作,核心方法在于addWaiter()和enq(),同时enq()完成对同步队列的头结点初始化工作以及CAS操作失败的重试;
    2. 线程获取锁是一个自旋的过程,当且仅当 当前节点的前驱节点是头结点并且成功获得同步状态时,节点出队即该节点引用的线程获得锁,否则,当不满足条件时就会调用LookSupport.park()方法使得线程阻塞;
    3. 释放锁的时候会唤醒后继节点;

    总体来说:在获取同步状态时,AQS维护一个同步队列,获取同步状态失败的线程会加入到队列中进行自旋;移除队列(或停止自旋)的条件是前驱节点是头结点并且成功获得了同步状态。在释放同步状态时,同步器会调用unparkSuccessor()方法唤醒后继节点。


    可中断式获取锁

    可响应中断式锁可调用方法lock.lockInterruptibly(),而该方法其底层会调用AQS的acquireInterruptibly方法。

    public final void acquireInterruptibly(int arg)
        throws InterruptedException {
      if (Thread.interrupted())
        throw new InterruptedException();
      if (!tryAcquire(arg))
        // 线程获取锁失败
        doAcquireInterruptibly(arg);
    }

    获取同步状态失败后就会调用doAcquireInterruptibly方法

    private void doAcquireInterruptibly(int arg)
    	throws InterruptedException {
    	// 将节点插入到同步队列中
    	final Node node = addWaiter(Node.EXCLUSIVE);
    	boolean failed = true;
    	try {
    		for (;;) {
    			final Node p = node.predecessor();
    			// 获取锁出队
    			if (p == head && tryAcquire(arg)) {
    				setHead(node);
    				p.next = null; // help GC
    				failed = false;
    				return;
    			}
    			if (shouldParkAfterFailedAcquire(p, node) &&
    			    parkAndCheckInterrupt())
    			    // 线程中断异常
    			    throw new InterruptedException();
    			}
    	} finally {
    	     if (failed)
    		cancelAcquire(node);
    	}
    }

    上述代码与acquire方法逻辑几乎一致,唯一的区别当parkAndCheckInterrupt返回true时(即线程阻塞时)该线程被中断,代码抛出被中断异常


    超时等待式获取锁

    通过调用lock.tryLock(timeout,TimeUnit)方式达到超时等待获取锁的效果,该方法会在三种情况下才会返回:
    1. 在超时时间内,当前线程成功获取了锁;
    2. 当前线程在超时时间内被中断;
    3. 超时时间结束,仍未获得锁返回false。

    该方法会调用AQS的方法tryAcquireNanos()。

    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
      if (Thread.interrupted())
        throw new InterruptedException();
      return tryAcquire(arg) ||
        // 实现超时等待的效果
        doAcquireNanos(arg, nanosTimeout);
    }

    最终是靠doAcquireNanos方法实现超时等待的效果:

    private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
      if (nanosTimeout <= 0L)
        return false;
      // 1.根据超时时间和当前时间计算出截止时间
      final long deadline = System.nanoTime() + nanosTimeout;
      final Node node = addWaiter(Node.EXCLUSIVE);
      boolean failed = true;
      try {
        for (;;) {
          final Node p = node.predecessor();
          // 2.当前线程获得锁出队列
          if (p == head && tryAcquire(arg)) {
            setHead(node);
            p.next = null; // help GC
            failed = false;
            return true;
         }
          // 3.1 重新计算超时时间
          nanosTimeout = deadline - System.nanoTime();
          // 3.2 已经超时返回false
          if (nanosTimeout <= 0L)
            return false;
          // 3.3 线程阻塞等待
          if (shouldParkAfterFailedAcquire(p, node) &&
            nanosTimeout > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanosTimeout);
          // 3.4 线程被中断抛出被中断异常
          if (Thread.interrupted())
            throw new InterruptedException();
       }
     } finally {
        if (failed)
          cancelAcquire(node);
     }
    }      

    通过上述代码我们可以发现,doAcquireNanos方法的程序流程如下:
    在这里插入图片描述
    程序逻辑同独占锁可响应中断式获取基本一致,唯一的不同在于获取锁失败后,对超时时间的处理上


    注:上述内容仅为自己在学习过程中的理解,如有不足之处,请指正。

    展开全文
  • 参考链接:https://www.freesion.com/article/4118592853/
  • AbstractQueuedSynchronizer(AQS)队列同步器 手写自旋锁: public class SpinLock { private AtomicReference<Thread> cas = new AtomicReference<Thread>(); public void lock() { Thread.....
  • import java.util.Random; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; public class HXWait { public static void main(String[] args) throws ExecutionException, Int...
  •  类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。  以下是本文的目录大...
  • Java多线程AQS

    2019-04-09 16:51:00
    Java多线程:线程间通信之Lock中我们提到了ReentrantLock是API级别的实现,但是没有说明其具体实现原理。实际上,ReentrantLock的底层实现使用了AQS(AbstractQueueSynchronizer)。AQS本身仅仅是一个框架,定义了...
  • Java多线程——AQS

    2020-08-08 09:27:03
    Java 并发包中很锁都是通过 AQS 来实现加锁和释放锁的过程的,AQS 就是并发包基础。例如 ReentrantLock、ReentrantReadWriteLock 底层都是通过 AQS 来实现的。 AQS 内部其实就包含了三个组件: state 资源状态...
  • java多线程AQS

    2021-05-14 14:05:49
    前提是得对 synchronized底层原理比较熟悉,其原理和AQS很像,感觉AQS就是一个基于Java的monitor实现。那么接下来我将类比monitor的结构来记录我对AQS流程的理解。 AQS 首先什么是AQS,个人认为就是一个类似monitor...
  • Java多线程-AQS

    2021-05-19 22:21:17
    1、AQS
  • Java多线程 - AQS详解

    2019-06-18 08:31:01
    AQS是java.util.concurrent.locks下类AbstractQueuedSynchronizer的简称,是用于 通过Java源码来构建多线程的锁和同步器的一系列框架,用于Java多线程之间的同步,它的类及类结构图如下: 原理 在AQS类中...
  • java多线程——AQS

    2020-08-08 16:45:35
    AQS:抽象同步队列,这是所有锁实现的基础。 该数据结构和操作系统中关于PV操作时的所需的数据结构一样。 首先,其内部有一个状态变量state,用于记录当前的锁是否有线程在是否,并且还有一个Thread变量thread来记录...
  • state 以 volatile 类型修饰,可以在多线程之间提供可见性。 ReentrantLock 和 CountDownLatch 对 state 的访问方式分为独占和共享两种,本文虽然没有解析 CountDownLatch 的源码,但通过上面源码的分析,可以想到...
  • AQS源码分析 AQS...voaltile state 是为了保证state变量线程的可见性, AQS改变state的方法主要有以下几个 getState() setState() compareAndSetState() 采用CAS添加节点到队列中好处 不...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,684
精华内容 673
关键字:

java多线程aqs

java 订阅