关于老师举例线程池+Semaphore配合使用的问题
来源:10-5 Semaphore用法和注意点

正义柔情永在
2020-03-01
老师,虽然直接上代码,但是很简单就10行左右,我的疑问在注释中
public class SemaphoreTest {
public static void main(String[] args) {
Random random = new Random(System.currentTimeMillis());
//凭证管理器,最多允许2个线程同时获取到凭证
Semaphore semaphore = new Semaphore(2);
//创建大小为5的线程池,并且缓存队列容量为0
ThreadPoolExecutor executors = new ThreadPoolExecutor(
5,
5,
0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<>(),
new ThreadPoolExecutor.AbortPolicy());
//提交6个任务
for (int i = 0; i < 6; i++) {
try {
executors.submit(()->{
try {
/**
* 此处会阻塞,也就是限制了后续业务访问能力,通过串行方式运行。
* 但是无法限制提交任务,如果要限制提交,则将拒绝策略换成{@link ThreadPoolExecutor.CallerRunsPolicy}
* 那么任务就会阻塞在提交线程中执行,从而限制了提交能力。
*
* TODO 我的问题是:如果一开始就只允许2个线程并发访问后续业务方法,为什么一开始不直接创建2个大小的线程池就行了,而是
* TODO 创建一个更大的线程池,而又在任务内部进行阻塞呢?
*/
semaphore.acquire();
Thread.sleep(random.nextInt(1000));
System.out.println(Thread.currentThread().getName()+"执行完毕。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
});
}catch (Exception e){
System.err.println(e.getMessage()); //Rejected 提交速度超出线程池能力,采用AbortPolicy 抛出异常
}
}
executors.shutdown();
}
}
写回答
1回答
-
悟空
2020-03-01
这是一个非常好的问题。让我们举一个在实际业务中会遇到的例子。比如我们在调用该“慢服务”前,需要有判断条件,比如我们只想在每天的0点整才会去访问这个“慢服务”,假设这个“慢服务”是“检查今天是否是星期日”。那么在大部分情况下(除了每天的0点),我们想要有更多的并发线程来提高性能,所以线程池的数量应该设置得大一些,而不仅仅是3个线程。此时我们就可以利用Semaphore来达到这个目的。
类似地,在大型应用程序中,会有不同类型的任务,通过不同的线程池来调用我们的“慢服务”,我们的调用方不仅仅是一个人,或者调用我们的直接是Tomcat服务器或者网关,我们就更不能限制,也不应该限制调用方的线程数量。
而一旦我们使用了Semaphore,由于信号量具有跨线程(跨线程池)的特性,那么即使这些请求来自于不同的线程池,我们也可以限制它们的访问。
基于以上的理由,如果想限制并发访问的线程数,用并发原语Semaphore是更合适的。
30
相似问题