Semaphore
锁状态的初始化
1 | public Semaphore(int permits) { |
将传递进来的参数,permit, 作为, state 字段的初始值。表示可以有几个线程获取锁,或者说是:state的值表示,还剩几个锁可以被直接获取到。当 state == 0 的时候,表示Semaphore持有的锁已经全部被使用了,再没有可以使用的锁的,此时如果一个线程请求获取锁,则这个线程将进行入 snyc queue,从而进入 block 状态。
acquire
acquire 方法最终调用: java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly 方法来获取锁。
和 ReentrantLock 不同的是,这里的 state 的语义发生的变化。
release
1 | public final boolean releaseShared(int arg) { |
doReleaseShared
释放锁是实现信号量(Semaphore)的关键。对于互斥锁来说,release 就是释放 head 处的 node 就可以了。
但是对于,共享锁来说,一次 tryRelease(permits) 调用有可以释放多个锁,例如 r 个锁,所以至少可以有 r 个线程来进行调度了。所以共享锁(Semaphore)的释放是比较复杂的。
其基本思路就是不断的释放 head.直到 head 不变为止。head 不发生变化有两种情况:
head 已经是最后一个结点了
锁使用完了,state == 0 了。
doReleaseShared 在线程 A 上调用。
线程 B, C, D 在 doAcquireSharedInterruptibly方法的
parkAndCheckInterrupt 处自旋等待。
1 | private void doReleaseShared() { |
至少有一个线程会因为上面的 doReleaseShared 调用而被唤醒,所以上面出现的锁空闲的问题就交给这个被唤醒的线程来继续完成。
1 | private void setHeadAndPropagate(Node node, int propagate) { |
这样 释放锁的线程的 doReleaseShared 方法调用 和获得锁的线程的 setHeadAndPropagate 方法调用,会相互激发调用,使得 信号量 所拥有的空闲锁尽快被等待的线程所持有,从而提高的并发的性能。
文档
semaphore 将 state 字段的语义定义为:可以并发访问共享资源的线程个数。
acquire 方法调用会减少 permit 的可用个数。
release 方法可以增加 permit 的可用个数。
Semaphore(信号量)对同步语义的实现和 ReentrantLock (可重入锁) 完全不同。Semaphore 实现的同步控制是对共享资源的并发访问线程个数的同步。所以 Semaphore 所获取的permit 并没有 ReentrantLock 的 lock 功能。所以对于资源的并发修改进行同步还需要互斥锁进行同步。不会维护数据一致性。
Note that no synchronization lock is held when acquire() is called as that would prevent an item from being returned to the pool. The semaphore encapsulates the synchronization needed to restrict access to the pool, separately from any synchronization needed to maintain the consistency of the pool itself.
如果一个信号量的 permit 是 1,则这个信号量也被称为:binary semaphore. 以这种方式使用 semaphor 时,相当于一个互斥锁,因为任意时刻,只有一个线程可以获取锁。
信号量并没有所有权的概念。
内存一致性效应:Actions in a thread prior to calling a “release” method such as release() happen-before actions following a successful “acquire” method such as acquire() in another thread.