J.U.C锁之 Semaphore
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了J.U.C锁之 Semaphore相关的知识,希望对你有一定的参考价值。
参考技术ASemaphore 名为"信号量"。
Semaphore用来管理内部许可证,当多个线程要访问竞争资源时可以通过Semaphore来控制并发访问竞争资源的线程数。当线程需要访问竞争资源时需要首先获取一个许可证,执行完毕在返还,如果许可证用完则,线程进入同步队列并阻塞。等待许可证返回唤醒。
主要特性
公平性 :支持公平性和非公平性。所谓公平表示在获取锁时逻辑是否要考虑当前正在排队等待线程。按照大白话来说就时公平表示不能插入强占资源。
应用场景
获取许可
释放许可
其他方法
Semaphore 使用AQS实现锁机制,AQS是AbstractQueuedSynchronizer的缩写,翻译过来就是"同步器",,它实现了Java函数中锁同步(synchronized),锁等待(wait,notify)功能。
AbstractQueuedSynchronizer是一个抽象类,我们可以编写自己类继承AQS重写获取独占式或共享式同步状态模板方法,实现锁锁同步(synchronized),锁等待(wait,notify)功能
AQS核心是一个同步状态,两个队列。它们实现了Java函数中锁同步(synchronized),锁等待(wait,notify),并在其基础上实现了独占式同步,共享式同步2中方式锁的实现。
无论独占式还时共享式获取同步状态成功则直接返回,失败则进入CLH同步队列并阻塞当前线程。当获取同步状态线程释放同步状态,AQS会选择从CLH队列head头部节点的第一个节点释放阻塞,尝试重写竞争获取同步状态,如果成功则将当前节点出队。如果失败则继续阻塞。
获取同步状态的线程也可以使用condition对象释放同步状态进入等待队列。只有等待其他线程使用condition.signal或condition.signAll()唤醒被从阻塞状态中释放重新竞争获取同步状态成功后从原来指令位置继续运行。
AQS实现了锁,必然存在一个竞争资源。AQS存在从一个int类型的成员变量state,我们把它称为同步状态,同步状态通常用做判断线程能否获取锁的依据
AQS 实现了锁那么总需要一个队列将无法获取锁的线程保存起来,方便在锁释放时通知队列中线程去重新竞争锁。
实现原理
同步队列又被称为CLH同步队列,CLH队列是通过 链式方式实现FIFO双向队列 。当线程获取同步状态失败时,AQS则会将当前线程构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态被释放时,会把首节点后第一个节点的线程从阻塞状态下唤醒,唤醒的线程会尝试竞争同步状态,如果能获取同步状态成功,则从同步队列中出队。
实现原理
这里取消节点表示当前节点的线程不在参与排队获取锁。
从概念上来说独占式对应只存在一个资源,且只能被一个线程或者说竞争者占用.
从概念上来说共享式对应存在多个资源的是有多个线程或者竞争者能够获取占用.
我们可以编写自己类继承AQS选择重写独占式或共享式模板方法,从而定义如何获取同步状态和释放同步状态的逻辑。
tryAcquire :尝试独占式获取同步状态,返回值为true则表示获取成功,否则获取失败。
tryRelease :
尝试独占式释放同步状态,返回值为true则表示获取成功,否则获取失败。
tryAcquireShared :尝试共享式获取同步状态,当返回值为大于等于0的时获得同步状态成功,否则获取失败。
tryReleaseShared :尝试共享式释放同步状态,返回值为true则表示获取成功,否则获取失败。
由于多个线程可以同时许可同时执行,当然我们选择使用共享同步,Sync需要重写 tryAcquire 获取同步状态条件逻辑, tryRelease 释放同步条件逻辑。其核心点在于使用同步状态做判断。当同状态为0时,许可被使用完了,同步状态大于0,许可被还可用,每次调用 tryAcquire 同步状态-1,每次调用 tryRelease 同步状态+1
内部存在有三个内部类 Sync、NonfairSync 和 FairSync 类。
Semaphore 很多方法都通过代理内部类的方法实现。
公平信号量获取许可
非公平信号量获取许可
释放许可
完整源码
以上是关于J.U.C锁之 Semaphore的主要内容,如果未能解决你的问题,请参考以下文章