网站访问者qq,常州企业自助建站,杭州seo网站排名,中建八局第二建设有限公司#x1f3f7;️个人主页#xff1a;牵着猫散步的鼠鼠 #x1f3f7;️系列专栏#xff1a;Java全栈-专栏 #x1f3f7;️个人学习笔记#xff0c;若有缺误#xff0c;欢迎评论区指正 目录
1. 前言
2. 什么是Semaphore#xff1f;
3. Semaphore源码解读 3.1 acquire… ️个人主页牵着猫散步的鼠鼠 ️系列专栏Java全栈-专栏 ️个人学习笔记若有缺误欢迎评论区指正 目录
1. 前言
2. 什么是Semaphore
3. Semaphore源码解读 3.1 acquire():获取许可
3.2 release():释放许可
4. Semaphore的使用
5. 总结 1. 前言
在前面的文章我们讲解了CountDownLatch倒计时器的基本使用本节我们继续来学习Java多线程编程中的一个工具类Semaphore信号量。
2. 什么是Semaphore
Semaphore 是一个在多线程环境中用于控制对共享资源的访问的同步器synchronizer它是 Java 5 中引入的 java.util.concurrentJUC包的一部分。Semaphore 维护了一个许可集线程在执行前必须从 Semaphore 获取一个许可。如果没有许可可用线程将阻塞等待直到其他线程释放许可。可以用来限制数据库连接数、限制服务器可处理请求数等。
3. Semaphore源码解读
我们跟进信号量的源码中浏览一圈发现其实它内部主要的方法就2个
// 初始共享资源数量
final Semaphore semaphore new Semaphore(5);
// 获取1个许可
semaphore.acquire();
// 释放1个许可
semaphore.release();3.1 acquire():获取许可
/*** 获取1个许可证*/
public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
跟进这个方法后我们会发现其内部调用了AQS的一个final 方法acquireSharedInterruptibly()
/*** 共享模式下获取许可证获取成功则返回失败则加入阻塞队列挂起线程*/
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();// 尝试获取许可证arg为获取许可证个数当可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列挂起当前线程。if (tryAcquireShared(arg) 0)doAcquireSharedInterruptibly(arg);
}我们可以发现这个方法中又调用了tryAcquireShared(arg)方法作为AQS中的钩子方法这个方法的实现在Semaphore的两个静态内部类 FairSync公平模式 和 NonfairSync非公平模式 中。虽然这个方法在AQS中但它作为钩子方法最终的实现则回到了Semaphore的内部类中。
我们接下来继续查看在 FairSync公平模式中的tryAcquireShared方法
static final class FairSync extends Sync {private static final long serialVersionUID 2014338818796000944L;FairSync(int permits) {super(permits);}protected int tryAcquireShared(int acquires) {for (;;) {if (hasQueuedPredecessors())return -1;// 获取可用的许可数int available getState();// 计算扣减后的许可数int remaining available - acquires;if (remaining 0 ||compareAndSetState(available, remaining))return remaining;}}}
这里就是获取剩余的许可数值然后计算扣减后的许可数值如果扣减后的值小于0就会直接返回扣减数(||短路或前者满足就不用判断后者了)。否认就会执行CAS操作compareAndSetState(available, remaining)扣减许可数的值。
这里我们就可知Semaphore信号量的许可数是存放在AQS中的state变量中的(AQS源码我后续应该也会发文讲解)然后通过CAS来修改state的值保证了操作的原子性。 3.2 release():释放许可
同样跟入这个方法里面用了AQS的releaseShared()而在这个方法内也毫无疑问的用了tryReleaseShared(int arg)这个钩子方法原理同上不再冗释需要注意的是释放共享锁(也就是增加许可数state的值)的同时也会唤醒同步队列中的一个线程。
// 释放一个许可证
public void release() {sync.releaseShared(1);
}// 释放共享锁同时会唤醒同步队列中的一个线程。
public final boolean releaseShared(int arg) {// 尝试释放共享锁(增加许可数)if (tryReleaseShared(arg)) {//唤醒同步队列中的一个线程doReleaseShared();return true;}return false;
}
protected final boolean tryReleaseShared(int releases) {for (;;) {int current getState();int next current releases;// 溢出情况判断if (next current) // overflowthrow new Error(Maximum permit count exceeded);if (compareAndSetState(current, next))return true;}}
4. Semaphore的使用
OK讲到这里把信号量中主要的方法解释完了我们来写一个小demo感受一下它的使用
public class Test {private final Semaphore semaphore;/*构造一个令牌*/public Test(int acq){this.semaphore new Semaphore(acq);}public void useSemaphore(){try {semaphore.acquire();// 使用资源System.out.println(资源使用 Thread.currentThread().getName());Thread.sleep(1000); // 模拟资源使用时间} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();System.out.println(资源释放 Thread.currentThread().getName());}}public static void main(String[] args) {Test test new Test(3);for (int i 0; i 5; i) {new Thread(test::useSemaphore).start();}}
}输出
资源使用 Thread-0
资源使用 Thread-1
资源使用 Thread-3
资源释放 Thread-0
资源使用 Thread-2
资源使用 Thread-4
资源释放 Thread-1
资源释放 Thread-3
资源释放 Thread-4
资源释放 Thread-25. 总结
Semaphore是基于AQS和CAS操作实现的共享锁利用AQS中被volatile修饰的变量state来代表许可证数(permit)当用户调用acquire()方法来获取许可证数时他会计算出本次取出操作后的许可证数如果小于0那么就会挂起等待如果大于0也就是许可证数还够用那么就会使用CAS操作修改许可证数。当调用release()方法释放许可证数时会唤醒等待的线程去获取共享锁。
如果本文对你有帮助点个小赞吧后续有时间就多出出这方面的源码解读