1。使用ThreadPoolExecutor时注意异常捕获线程池,如ThreadPoolExecutor.execute(newRunnable());如果异常没有被捕获,很容易造成异常的丢失,比如线程池如果队列满了,达到最大线程数,线程就会拒绝执行任务。如果此时不捕获execute()的异常,很容易导致任务未完成,没有异常,所以可以选择在执行过程中捕获异常。尝试{ThreadPoolExecutor.execute(newRunnable());}catch(Exceptione){log.warn("executorexception:",e);//补偿执行doCompensationWork();}2.线程池创建方式的选择:当需要创建线程池时,Java可以选择两种创建方式2.1通过Executors创建线程池:但是通过Executors创建线程池是有风险,因为:(摘自阿里编码协议)不允许使用Executors来创建线程池,而通过ThreadPoolExecutor方法,这种处理方式可以让写的同学更清楚线程池的运行规则和避免资源枯竭的风险。2.2使用newThreadPoolExecutor创建线程池创建方法:其实Executors底层也是使用ThreadPoolExecutor创建的。使用ThreadPoolExecutor创建需要指定核心线程数、最大线程数、线程空闲时间、阻塞队列。这样就可以根据实际使用场景设置需要的核心线程和队列大小,避免内存占用过大或线程过多导致的OOM风险。所以首选ThreadPoolExecutor来创建线程池。3Executors各方法缺点原因3.1newFixedThreadPool和newSingleThreadExecutor:主要问题是请求处理队列累积起来可能会消耗非常大的内存,甚至OOM。查看它的源码可以看到newFixedThreadPool和newSingleThreadExecutor都是使用LinkedBlockingQueue作为线程池的队列。核心线程写满后,任务会存放到队列中,这可能会导致队列存放多个任务,导致内存泄漏甚至OOM。风险。3.2newCachedThreadPool和newScheduledThreadPool:主要问题是最大线程数是Integer.MAX_VALUE,可能会创建非常多的线程,甚至OOM。四、ThreadPoolExecutor参数分析:1.corePoolSize&maximumPoolSize核心线程数(corePoolSize)和最大线程数(maximumPoolSize)当有新任务提交到池中时,如果当前运行的线程数小于核心线程(corePoolSize),即使当前有空闲线程也会创建一个新线程来处理新提交的任务;如果当前运行的线程数大于核心线程数(corePoolSize)小于最大线程数(maximumPoolSize),只有当等待队列满了才会创建新的线程,也就是大于的时候比核心线程先入队列,当队列满时再创建一个新线程,直到达到最大线程数。这时候,如果任务又来了,就会执行设置的拒绝策略;2.keepAliveTime&unitkeepAliveTime是超过corePoolSize线程最大空闲时间的线程数,unit是时间单位。非核心线程的最大空闲时间;3.等待队列任何阻塞队列(BlockingQueue)都可以用来传递或保存提交的任务。线程池的大小和阻塞队列相互约束线程池:如果正在运行的线程数小于corePoolSize,则提交新任务时会创建一个新线程运行;如果正在运行的线程数大于等于corePoolSize,则新提交的任务会进入队列等待;如果队列已满,且正在运行的线程数小于maximumPoolSize,则创建一个新线程运行;如果线程数大于maximumPoolSize,新提交的任务将按照拒绝策略进行处理。4.直接传递三种通用入队策略:通过SynchronousQueue直接传递任务给线程。如果当前没有线程可用,则入队尝试将失败并创建一个新线程。该策略可以防止请求在处理可能具有内部依赖性的请求时被锁定。传递通常需要无限制的最大线程数(maximumPoolSize)以避免拒绝新提交的任务。当任务继续以比它们可以处理的速度更快的平均速度到达时,这可能会导致线程的无限增长。无界队列:使用无界队列(如LinkedBlockingQueue)作为等待队列。当所有核心线程都在处理任务时,新提交的任务会进入队列等待。因此,不会创建大于corePoolSize的线程(maximumPoolSize也没有效果)。这种策略适用于每个任务完全独立于其他任务的情况;比如网络服务器。这种类型的等待队列可以平滑突发的高频请求。当任务继续以比它们可以处理的速度更快的平均速度到达时,这可能会导致等待队列无限增长。有界队列:有界队列如ArrayBlockingQueue可以在使用有限的最大线程数时防止资源耗尽,但难以调优和控制。队列大小和线程池大小可以相互作用:使用大队列和少量线程可以减少CPU使用率、系统资源和上下文切换开销,但如果任务频繁阻塞(例如通过I/Olimit),系统可以调度更多线程的执行时间。使用小队列通常需要更多的线程,这样可以最大限度地提高CPU使用率,但可能需要更大的调度开销,从而降低吞吐量。5、拒绝策略当线程池关闭或饱和时(最大线程和队列都已满),新提交的任务将被拒绝。ThreadPoolExecutor定义了四种拒绝策略:AbortPolicy:默认策略,当需要拒绝任务时抛出RejectedExecutionException;CallerRunsPolicy:直接在execute方法的调用线程中运行被拒绝的任务。如果线程池关闭,任务将被丢弃;DiscardPolicy:直接丢弃任务;DiscardOldestPolicy:丢弃队列中等待时间最长的任务,执行当前提交的任务。如果线程池关闭,任务将被丢弃。我们也可以自定义拒绝策略,只需要实现RejectedExecutionHandler;需要注意的是,拒绝策略的运行需要指定线程池和队列的容量。
