多线程的由来:
由于cpu和磁盘IO的处理速度差异很大,所以支持多进程和多线程的方式来提高硬件使用效率。
虽然操作系统本身已利用高速分级缓存和cpu自动切换时间片的机制。
但是有些特定场景还是需要根据不同程序的来自己配置多线程策略。
主要看压测的结果,是io密集还是cpu密集型。
java多线程常用的2个线程池:(创建线程比较耗时,所以用配置线程池的方式解决)
不推荐用Executors线程池,最好用ThreadPoolExecutor线程池。
Executors简化了创建线程池的方式,他主要有4种,常用的2种都是无界队列,有oom的风险。
推荐自己配置ThreadPoolExecutor线程池,指定拒绝策略。在任务超过的时候,按照自己希望的方式处理,而不影响整个服务。
4种拒绝策略:
AbortPolicy 是直接抛异常,
DiscardPolicy 是直接丢弃当前线程,
DiscardOldestPolicy 是抛弃最旧的,
CallerRunsPolicy是满了之后变成同步执行(一般用这个)
最佳实践示例:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("pim-taskExecutor-%d").build();
//参数依次释义:50核心线程数量,200最大线程数,60线程活跃时间,等待队列容量1024,Factory设置名称,拒绝策略(超过1024后)
ExecutorService executorService1 = new ThreadPoolExecutor(50, 200,60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
List<CreateProductsCallable> callAbles = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
CreateProductsCallable callable = new CreateProductsCallable("", new ProductDataVO(), null, null);
callAbles.add(callable);
}
List<Future<CreateProductBatchResponseVO.CodeVO>> futures = null;
try {
//超过这里30000时间限制的线程会被取消 CancellationException
futures = executorService1.invokeAll(callAbles, 30000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.error("多线程中断异常:{}"+Thread.currentThread().getName() + e.getMessage());
Thread.currentThread().interrupt();
}
核心线程数量参考压测结果:响应时间、吞吐量
IO密集型: 多线程最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]
CPU密集型:理论上(纯cpu处理)“线程的数量 =CPU 核数”就是最合适的。不过在工
程上,线程的数量一般会设置为“CPU 核数 +1”,这样的话,当线程因为偶尔的内存页失效或
其他原因导致阻塞时,这个额外的线程可以顶上
ps:Executors简化了创建线程池的方式,他主要有4种,single和fixed,用的无界队列,不存在拒绝。cached,来一个任务建一个线程,不存在拒绝,还有个是schedule吧,用的不多,不了解