• JAVA线程池使用不当会造成哪些后果?
  • 发布于 1个月前
  • 69 热度
    0 评论
  • 亦東風
  • 0 粉丝 38 篇博客
  •   

废话不多说,先上代码。

反例 1:

public class ThreadPoolExample {
    // 堆代码 duidaima.com
    // 没有任何限制的线程池, 使用起来很方便, 但当一波请求高峰到达时, 可能会创建大量线程, 导致系统崩溃
    private static Executor executor = Executors.newCachedThreadPool();

}
反例 2:
public class StreamParallelExample {

    public List<String> batchQuery(List<String> ids){
        // 看上去很优雅, 但 ForkJoinPool 的队列是没有大小限制的, 并且线程数量很少, 如果 ids 列表很大可能导致 OOM
        // parallelStream 更适合计算密集型任务, 不要在任务中做远程调用
        return ids.parallelStream()
            .map(this::queryFromRemote)
            .collect(Collectors.toList());
    }

    private String queryFromRemote(String id){
       // 从远程查询
    }
}
手动创建线程池
正例:
public class ManualCreateThreadPool {
    
    // 手动创建资源有限的线程池
    private Executor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000),
        new ThreadFactoryBuilder().setNameFormat("work-%d").build());
}


总结

线程池使用不当可能导致一系列问题,包括性能下降、资源耗尽、甚至程序崩溃。以下是一些常见的线程池使用不当的情况及其后果:
1.线程池大小设置不合理:
线程池过大:过多的线程会占用大量的系统资源,包括CPU和内存,导致系统性能下降,甚至引发OutOfMemoryError。
线程池过小:线程数不足以处理任务,导致任务等待时间过长,响应延迟,吞吐量下降。
2.任务队列容量不当:
队列容量过大:过多的任务在队列中等待执行,占用大量内存,可能导致内存溢出。
队列容量过小:任务被拒绝服务,可能导致服务不可用或数据丢失。
3.任务执行时间过长:
如果线程池中的任务执行时间过长,会导致线程长时间被占用,无法处理其他任务,从而降低系统的吞吐量。
4.未合理处理异常:
线程执行任务时可能会抛出异常,如果没有适当的异常处理机制,可能会导致线程终止,进而减少线程池中的可用线程数。
5.未关闭线程池:
在应用程序结束时,如果没有正确关闭线程池,可能会导致后台线程继续运行,造成资源泄露。
6.线程池类型选择不当:
根据任务类型(如IO密集型或计算密集型)选择合适的线程池类型非常重要。使用不合适的线程池类型可能导致资源利用率低下。
7.未限制任务提交速度:
如果任务提交速度过快,超过线程池的处理能力,可能导致任务堆积,甚至引发内存溢出。


为了避免这些问题,需要在使用线程池时:
1.根据系统资源和任务特性合理设置线程池大小和队列容量。
2.确保任务执行时间可控,避免长时间运行的任务。
3.实现适当的异常处理机制,确保线程的稳定性。
4.在应用程序结束时,正确关闭线程池,释放资源。
5.根据任务类型选择合适的线程池类型。
6.限制任务提交速度,避免任务堆积。


总之,线程池是一把双刃剑,使用得当可以显著提高系统性能,但使用不当也可能带来一系列问题。因此,在使用线程池时,需要仔细考虑和合理配置相关参数,以确保系统的稳定性和高效性

用户评论