前几天群里有网友问我信号量和线程池,我对信号量回答有些错误,后面群里其他网友做了更正,今天由于时间紧张,我就简单的在说一下信号量和线程池。
自从 jdk5.0 开始在 java.util.concurrent 包里提供了 Semaphore 的官方实现,因此大家不需要自己去实现 Semaphore。但是还是很有必要去熟悉如何使用 Semaphore 及其背后的原理。Semaphore(信号量)是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失,或者像锁一样用于保护一个关键区域。
信号量和线程池的使用场景非常的广泛,最简单的就是 Hystirx 中提供了两种模式执行逻辑:信号量、线程池。所以对于 JUC 这套视频,大家还是很有必要看一看的。
Semaphore 可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在 Windows 下可以设置共享文件的最大客户端访问个数。
Semaphore 实现的功能就类似厕所有 5 个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当 5 个人中的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造 Semaphore 对象时传入的参数选项。单个信号量的 Semaphore 对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
Semaphore 维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用 Semaphore 可以实现有限大小的链表。另外重入锁 ReentrantLock 也可以实现该功能,但实现上要复杂些。
package com.xttblog; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; class Employee implements Runnable { private String id; private Semaphore semaphore; private static Random rand= new Random(47); public Employee(String id, Semaphore semaphore) { this.id = id; this.semaphore = semaphore; } public void run() { try { semaphore.acquire(); System.out.println(this.id + "is using the toilet"); TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000)); semaphore.release(); System.out.println(this.id + "is leaving"); } catch (InterruptedException e) { } } } public class ToiletRace { // 定义10个人上厕所 private static final int THREAD_COUNT = 10; // :www.xttblog.com private static ExecutorService threadPool = Executors .newFixedThreadPool(THREAD_COUNT); // 定义5个坑位 private static Semaphore s = new Semaphore(5); public static void main(String[] args) { for (int i = 0; i < THREAD_COUNT; i++) { threadPool.execute(new Employee(String.valueOf(i), s)); } threadPool.shutdown(); } }
这里我定义了 10 个人要上厕所,但是只有 5 个坑位,每个人消耗随机的时间,直接运行上面这段代码,可以看到一开始进去了 5 个人,后来就是陆陆续续的有人进,有人出了。但是正在使用的一定不会超过 5 个的。
acquire()
public void acquire() throws InterruptedException
从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。获取一个许可(如果提供了一个)并立即返回,将可用的许可数减 1。
如果没有可用的许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:某些其他线程调用此信号量的 release()方法,并且当前线程是下一个要被分配许可的线程;或者其他某些线程中断当前线程。
如果当前线程:被此方法将其已中断状态设置为 on ;或者在等待许可时被中断。则抛出 InterruptedException,并且清除当前线程的已中断状态。
release()
public void release()
释放一个许可,将其返回给信号量。释放一个许可,将可用的许可数增加 1。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。然后针对线程安排目的启用(或再启用)该线程。
不要求释放许可的线程必须通过调用 acquire() 来获取许可。通过应用程序中的编程约定来建立信号量的正确用法。
另外两个不常用的方法,public int availablePermits()查看现在可用的信号量。public int drainPermits(),这个方法返回即可所有的许可数目,并将许可置 0。public Semaphore(int permits) 其中参数permits就是允许同时运行的线程数目。
: » 详解 java.util.concurrent.Semaphore 信号量
原创文章,作者:506227337,如若转载,请注明出处:https://blog.ytso.com/251954.html