述
Semaphore
信号量,也是一个线程协作的工具,可以用来限制或管理数量有限的资源的使用
用法
信号量的作用是维护一个 “许可证” 的计数,线程可以获取许可证,然后信号量剩余许可证数量减一,当信号量所拥有的许可证为0的时候,下一个想要获取许可证的线程就需要等待,直到有另外的线程释放了许可证
常用的几个方法如下
- 构造函数: 和上文的
CountDownLatch
一样,Semaphore
在初始化的时候也需要指定许可证的数量,还有一个构造参数是配置公平策略,如果是 true ,当有了新的许可证的时候,会把它给等待时间最长的线程 acquire()
/acquireUninterruptibly()
: 这两个方法是用来获取许可证的,从名字上来看就知道,后面的是可以在获取的过程种被打断的tryAcquire(timeout)
/tryAcquire()
: 这两个方法是尝试获取许可证,在规定时间内立即返回,不会阻塞release()
: 释放许可证
使用案例
用个具体的代码演示以下信号量的使用
@Slf4j
public class SemaphoreTest {static Semaphore semaphore = new Semaphore(3);public static void main(String[] args) {for (int i = 0; i < 100; i++) {new Thread(new Task()).start();}}@Slf4jstatic class Task implements Runnable{@Overridepublic void run() {try {semaphore.acquire();} catch (InterruptedException e) {e.printStackTrace();}log.info("获取到了许可证,执行具体逻辑...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.info("完成任务,释放许可证...");semaphore.release();}}
}
新建一个3个许可证的信号量,然后新建 100 个线程都去获取证书,执行后再释放掉,看一下控制台输出
11:03:17.556 [Thread-1] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:17.556 [Thread-0] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:17.556 [Thread-2] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:18.579 [Thread-0] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:18.579 [Thread-2] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:18.579 [Thread-1] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:18.579 [Thread-4] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:18.579 [Thread-5] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:18.579 [Thread-3] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:19.579 [Thread-3] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:19.579 [Thread-5] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:19.579 [Thread-4] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:19.579 [Thread-6] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:19.579 [Thread-7] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:19.579 [Thread-29] INFO com.learning.java.cooperation.SemaphoreTest$Task - 获取到了许可证,执行具体逻辑...
11:03:20.579 [Thread-6] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:20.579 [Thread-7] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
11:03:20.579 [Thread-29] INFO com.learning.java.cooperation.SemaphoreTest$Task - 完成任务,释放许可证...
可以看到每次执行的都是3个线程,释放之后才会有下一批线程获取到,这里上面构造函数中没有设置为公平的,所以这里获取到许可证的线程并不是按顺序的
注意点
acquire()
和release()
方法都有一个重载的方法,可以传入一个 int 类型的参数,表示获取/释放几个许可证,但是并不强制获取几个就得释放几个,比如获取两个释放一个也是可以的,但最后许可证会越来越少,会导致程序卡死- 初始化的时候一般设置为公平的,更加合理
- 并不是必须由获取许可证的线程释放许可证, 比如A线程获取了许可证,可以由B线程释放