这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
AQS的大概设计于synchorized不能说差不多,只能说一模一样,但是我们能到其中内部的一些技巧。
在之前分析的ConcurrentLinkedQueue中是无锁的单向队列,我本以为这已是巅峰,直到AQS出现了。
我一度怀疑这段代码是非并发安全,因为它做了三个操作,但我始终无法找到它的破绽。最终我是这么理解的:
- 线程安全问题只出现在全局变量写的情况下。
- 基于1,在看代码只有设置尾节点才是写全局变量,其它所用的变量都是线程的局部变量,没有一个会被线程公用。
如果不看源码,谁知道还能实现无锁双向队列。
但AQS为什么要使用双向队列?能否用单向队列实现??
关于队列的遍历操作发生在以下两个地方:
- cas抢占state失败,进入队列,进行CLH自旋时,会获取 prev 判断==head?
2. unlock释放锁,会重新设置head节点,然后唤醒next节点。
以上两个步骤其实是为了保证FIFO的顺序,即先进入的线程先获取到锁。比如,假若第一步没有 == head判断,这个地方可能同时会进入多个线程,cas成功的可能不是头节点.next(新的lock、interrupt唤醒、unlock唤醒导致进入多个线程)。第二个步骤的顺序唤醒如果没有,肯定不能保证FIFO的顺序。
那么能否不实现FIFO的顺序?其实这就是非公平锁,《并发编程的艺术》有提到公平非公平的区别,非公平锁通常都是同个线程会获取到同个锁。这也是比较容易理解,因为其它大部分线程都处于wait状态,或者刚刚进入lock,可能没有获取到CPU执行权限,而获取lock的线程是active状态,一定是获取了CPU执行权限,CAS更容易成功。
所以我又个设想,维护单向的队列,竞争失败还是入队tail节点,并且进行CLH自旋转,因为这部分操作是多线程。对于第二步出队列时,从tail节点进行唤醒。单向队列会减少额外维护指针的操作,是不是会有更高的性能?当然大前提是需要你的业务对线程饥饿无感。