带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(7)https://developer.aliyun.com/article/1340062?groupCode=taobaotech
避免使用局部线程池
使用局部线程池时,若任务执行完后没有执行shutdown()方法或有其他不当引用,极易造成系统资源耗尽。
合理设置线程池参数
在工程实践中,通常使用下述公式来计算核心线程数: nThreads=(w+c)/c*n*u=(w/c+1)*n*u
其中,w为等待时间,c为计算时间,n为CPU核心数(通常可通过 Runtime.getRuntime().availableProcessors ()方法获取),u为CPU目标利用率(取值区间为[0, 1]);在最大化CPU利用率的情况下,当处理的任务为计算密集型任务时,即等待时间w为0,此时核心线程数等于CPU核心数。
上述计算公式是理想情况下的建议核心线程数,而不同系统/应用在运行不同的任务时可能会有一定的差异,因此最 佳线程数参数还需要根据任务的实际运行情况和压测表现进行微调。
增加异常处理
为了更好地发现、分析和解决问题,建议在使用多线程时增加对异常的处理,异常处理通常有下述方案:
在任务代码处增加try...catch异常处理
如果使用的Future方式,则可通过Future对象的get方法接收抛出的异常
为工作线程设置setUncaughtExceptionHandler,在uncaughtException方法中处理异常
优雅关闭线程池
public void destroy() { try { poolExecutor.shutdown(); if (!poolExecutor.awaitTermination(AWAIT_TIMEOUT, TimeUnit.SECONDS)) { 5poolExecutor.shutdownNow(); } } catch (InterruptedException e) { // 如果当前线程被中断,重新取消所有任务 pool.shutdownNow(); // 保持中断状态 Thread.currentThread().interrupt(); } }
为了实现优雅停机的目标,我们应当先调用shutdown方法,调用这个方法也就意味着,这个线程池不会再接收任何新的任务,但是已经提交的任务还会继续执行。之后我们还应当调用awaitTermination方法,这个方法可以设定线程池在关闭之前的最大超时时间,如果在超时时间结束之前线程池能够正常关闭则会返回true,否则,超时会返 回false。通常我们需要根据业务场景预估一个合理的超时时间,然后调用该方法。
如果awaitTermination方法返回false,但又希望尽可能在线程池关闭之后再做其他资源回收工作,可以考虑再调用一下shutdownNow方法,此时队列中所有尚未被处理的任务都会被丢弃,同时会设置线程池中每个线程的中断 标志位。shutdownNow并不保证一定可以让正在运行的线程停止工作,除非提交给线程的任务能够正确响应中 断。
带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(9)https://developer.aliyun.com/article/1340060?groupCode=taobaotech