JAVA-AbstractQueuedSynchronizer

来源:互联网 发布:费用优化的步骤 编辑:程序博客网 时间:2024/06/06 03:10

Java的AbstractQueuedSynchronizer的lock动作,是通过CAS操作去修改一个int型整数,该整数表示一个状态,不同的子类对状态的含义可以有不同的解释。

挂起自己则是通过Unsafe类的park()方法(不是Object的wait()哦)来实现,该方法是一个native方法,具体实现在JDK中。

唤醒是通过Unsafe类的unpark()方法(也不是Object的notify()哦)来实现,该方法是一个native方法,具体实现在JDK中。这是因为,Object的wait()和notify()是与synchronized搭配使用的。

1、AQS的内部变量

head变量代指当前持有锁的线程;

tail标量代指队列尾部。

state 这是一个volatile型的整形状态变量,锁操作其实对应了这个整数的修改。


多个线程对同一个锁进行竞争时,若获取锁失败,则会添加到该锁的一个线程队列(队列的节点类型为java.util.concurrent.locks.AbstractQueuedSynchronizer#Node)的末尾(变量tail的后面),并且保持顺序不变,直到被移除掉(获得锁的线程所属节点,一定处于头位置,而新获取锁的线程总要先将旧的头节点移除,再将自己所属节点设置为头节点)。


2、此次要介绍的方法

AQS#lock()

AQS#acquire(int)

AQS#tryAcquire(int)

AQS#acquireQueued(...)

AQS#shouldParkAfterFailedAcquire()

AQS#release()


3、 AQS.acquire(int)

这个方法是个终极方法,不允许被覆盖,定义了以排队的方式获取锁的正常流程。



4、tryAcquire(int)

tryAcquire(int)是定义单次获取锁的具体操作,AQS要求子类自行实现。获得锁,则返回true,否则返回false。不管获取锁是否成功,当前线程在这个方法里都不会被挂起。



5、acquireQueued(...)

acquireQueued是循环地尝试获取锁(通过调用不断的调用tryAcquire(int)),直到获取锁,每一次尝试失败后都会被挂起,被唤醒后继续尝试。该方法不允许被覆盖



6、shouldParkAfterFailedAcquire(...)

该方法有如下功能:

1)、移除AQS队列中放弃竞争锁的节点。

2)、若当前节点的前驱节点的状态不是SIGNAL,则将其置为SIGNAL(SIGNAL表示当前节点的后继节点需要被挂起)。

上述两个功能不会在一次调用中同时完成。而是按上述顺序查找,找到一个完成,然后退出。该方法是在一个死循环体(acquireQueued方法)中被调用,若当前线程被唤醒但不具备获取锁的资时,那么最多两轮循环就会被再次挂起。


7、release()

当前线程(即head节点所属的线程)释放锁时,会唤醒其后继节点所属的线程。以继续执行。这体现在AQS的unparkSuccessor方法里。



8、公平锁 VS 非公平锁

这里的公平锁和非公平锁仅局限在java的默认实现,并不代表所有的都这样。

8.1 区别一:lock()

非公平锁会先不管三七二十一地尝试加锁。加锁失败后,才按照正常的排队获取所流程获取锁。




8.2 区别二:tryAcquire

公平锁在:1)在AQS的队列中,自己前面还有未获得锁的线程且当前持有锁的线程不是自己;2)尝试获取锁失败(有可能被其他非公平锁的线程抢占)的情况下,均不能获得锁。

公平锁的tryAcquire之所以会添加这个(红色加粗字体)约束条件,是因为:在进入AQS的acquireQueued方法之前会执行一次tryAcquire方法,若不加这个约束,就破坏了公平锁的公平性。在公平锁环境的acquireQueued方法里,能执行tryAcquire的线程都是紧随head之后的线程(额,就是线程释放锁之后只会唤醒紧随其后的线程,不会把所有的都唤醒),其hasQueuedPredecessors()一定返回false。



非公平锁,只要线程被唤醒,在每次的尝试获取锁过程中,不会去查看自己前面是否还有未获得锁的线程,而是直接尝试抢占。




8.3 区别三:release()

哦,应该是共同点,公平和非公平锁都没有自定义release方法,这就意味着,一旦线程进入排队等待的队列,不管是公平锁还是非公平锁,表现都一致。额,说的简单点,就是非公平锁每次也是唤醒紧随其后的那个线程。


1 0
原创粉丝点击