我遇到有个很消耗性能的功能,这个功能需要保证一定并发量 不能被单个请求占据太多资源。这个时候我们可以通过 Semaphore 控制线程并发量,来保证单个请求不被消耗太多资源。
我们介绍一下 Semaphore 最常使用的两个api
//创建可以同时容纳两个线程同时运行的并发控制器
Semaphore semaphore = new Semaphore(2);
//可以将acquire 视为拦截器的入口,只有当线程数低于 初始许可数量 的时候,允许通行
semaphore.acquire();
//可以 将release 视为拦截器的出口,当该线程释放了,就又可以 让拦截器入口,放一个线程通行
semaphore.release();
我们这里用个例子做一下示范:
public class SemaphoreTest {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ThreadPoolExecutor COMMON_EXECUTOR =
new ThreadPoolExecutor(5, 10, 200L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(20), new DefaultNamedThreadFactory("seaphoreTest"),
new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 10; i++) {
Integer j = i;
COMMON_EXECUTOR.submit(() -> {
try {
semaphore.acquire();
System.out.println(j + "进入线程了"+formatter.format(LocalDateTime.now()));
Thread.sleep(10000);
System.out.println(j+"释放了"+formatter.format(LocalDateTime.now()));
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+"has a error"+e.getMessage());
}finally {
semaphore.release();
}
});
}
}
}
查看运行结果:
可以看到到每次在正在同时运行
semaphore.acquire(); 和 semaphore.release(); 之间的代码永远只有两个线程。
调整 Semaphore semaphore = new Semaphore(2); 这个 new Semaphore(n) 的数量,
semaphore.acquire(); 和 semaphore.release(); 也随着改变。
2进入线程了2020-01-10 23:19:23
3进入线程了2020-01-10 23:19:23
2释放了2020-01-10 23:19:33
3释放了2020-01-10 23:19:33
4进入线程了2020-01-10 23:19:33
5进入线程了2020-01-10 23:19:33
5释放了2020-01-10 23:19:43
7进入线程了2020-01-10 23:19:43
4释放了2020-01-10 23:19:43
1进入线程了2020-01-10 23:19:43
7释放了2020-01-10 23:19:53
0进入线程了2020-01-10 23:19:53
1释放了2020-01-10 23:19:53
6进入线程了2020-01-10 23:19:53
0释放了2020-01-10 23:20:03
6释放了2020-01-10 23:20:03
8进入线程了2020-01-10 23:20:03
9进入线程了2020-01-10 23:20:03
8释放了2020-01-10 23:20:13
9释放了2020-01-10 23:20:13
附上自定义线程工厂的类,如果自己自定义线程池可以使用这个自定义的工厂类,当出现问题能够及时定位。
public class DefaultNamedThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public DefaultNamedThreadFactory(String nameElem) {
SecurityManager manager = System.getSecurityManager();
group = !Objects.isNull(manager) ? manager.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "pool-" + nameElem + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if(thread.getPriority()!=Thread.NORM_PRIORITY){
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}