新闻资讯

新闻资讯 行业动态

AQS是如何实现的?

编辑:008     时间:2020-02-18

1. AQS是如何将请求的线程封装成Node的呢?又是如何将Node连接成队列的呢?

  1. 既然是封装,那Node中便会持有一个请求Thread对象,并且为了建立Node之间的联系Node中会维护前置与后置节点指针,而AQS中会维护头尾节点指针,此时注意这里维护的是同步队列(在这个队列上的线程都在不断尝试是否可以获取到锁,因为在同步队列上便可以调用AQS的acquireQueue方法,而这个方法使得为获取到锁的线程检查自己是否有资格获取锁,如果没有,则调用LockSupport().park()方法将Node中的线程状态改为WAITING,等待被唤醒或被中断);
强调同步队列是因为,还有多个等待队列(与synchronized中Monitor对象的WaitSet一个意思,不过Monitor对象只有一个,而AQS可以有多个等待队列,视Conditon的数量为定),并且在Node节点中通过Condition指针维护,因为Node是同步队列与等待队列复用的,所以不可避免的产生了一些冗余;2. 注意:此时最好要将synchronized的monitor机制与这里的AQS机制联系起来看 在monitor机制中获得锁的线程如果调用wait()方法,该线程所持有的锁会被释放并将该线程加入等待队列中,而Condition是调用await()方法将该线程放入对应的Condition所持有的等待队列中去(我觉得可以把Condition理解成操作系统中定义的线程唤醒条件),所以有几个Condition就会有几个对应的等待队列;

2. AQS是如何维护共享变量的可访问性呢?

  1. 在独占锁中,只有在同步队列的首节点的next节点可以尝试获取共享变量,因为在acquireQueue()方法中是这样定义判断条件的
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;
                }
        } finally { if (failed)
                cancelAcquire(node);
        }
    } 

这样也就保证了获取共享资源的顺序性(即按照插入到队列的时间来定)

  1. 那AQS只能用来实现独占且公平锁吗?显然不是,AQS又是如何实现非公平锁和共享锁的呢?其实AQS无论用来实现什么锁,这些锁本质的区别就是在于获取共享资源访问权的方式不同,而独占且公平的锁很明显获取访问权的方式是通过FIFO队列的顺序(即请求访问共享资源的顺序),而共享锁也是一样,只是可以获取访问权的线程数多了些;那么非公平锁是如何实现的呢?其实也很简单,就是舍弃队列的FIFO特性,只要持有共享资源的线程释放了锁,所有的在同步队列中的线程都会通过CAS操作去竞争锁;


郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐