java并发编程实战——线程池的使用

线程死锁

(1)只有当任务都是同类型的并且相互独立时,线程池的性能才能达到最佳,如果提交的任务依赖于其他任务,除非线程池无限大,否则将可能造成死锁;网页服务器、邮件服务器、文件服务器等,它们的请求通常都是同类型并且相互独立的。(2)在单线程Executor中,如果一个任务将另一个任务提交给同一个Executor,并且等待这个被提交任务的结果,那么通常会引发死锁;第二个任务停留在工作队列中,并等待第一个任务完成,而第一个任务又无法完成,因为他在等待第二个任务完成。(3)线程饥饿死锁:所有正在执行任务的线程都由于等待其他仍处于工作队列中的任务而阻塞,导致死锁(4)运行时间较长的任务,极有可能使线程池的响应性变得很慢;限定任务等待资源的时间,而不要无限制地等待,能缓解执行时间较长任务造成的影响;

设置线程池大小:

线程池的理想大小取决于被提交任务的类型以及所部署系统的特性。(1)线程池过大,那么大量的线程将在相对很少的CPU和内存资源上发生竞争,可能导致过高的内存使用量以及耗尽资源(2)线程池过小,导致许多空闲的处理器无法执行任务,从而降低吞吐率(3)对于计算密集型的任务,当拥有N个处理器的系统,线程池大小为N+1,通常能实现最优的利用率(4)要使处理器达到期望的使用率,线程池的最优大小为:线程池大小 = CPU数量 * CPU利用率(大于等于0,小于等于1) * (1 + 任务等待时间/任务计算时间)CPU数量 = Runtime.getRuntime().availableProcessors();(5)如果每个任务都需要一个数据库连接,则数据库连接池的大小就限制于线程池的大小

ThreadPoolExecutor

是一个灵活的、稳定的线程池,创建ThreadPoolExecutor时,线程并不会立即启动,而是等到有任务时才会启动(1)newFixedThreadPool和newSingleThreadExecutor默认情况下使用一个无界的LinkedBolckingQueue队列来保存任务线程,可能会导致资源被耗尽(2)newCachedThreadPool使用SynchronousQueue,要将一个任务放入SynchronousQueue中,必须要有一个线程正在接受这个任务,如果没有线程正在等待且线程池大小小于最大值,那么ThreadPoolExecutor将创建一个新的线程,否则此任务将被拒绝。(3)LinkedBolckingQueue、ArrayBolckingQueue是先进先出队列,任务执行顺序与到达顺序相同;PriorityBolckingQueue是优先级队列,可以设置任务的优先级来确定任务的执行顺序

线程池饱和策略

等待队列满时,才会使用到饱和策略(1)AbortPolicy(中止策略):默认的饱和策略,会抛出RejectedExecutionException异常,调用者捕获此异常后,编写自己的处理代码(2)DiscardPolicy(抛弃策略):当新提交的任务,无法保存到队列中等待执行时,将抛弃该任务(3)DiscardOldestPolicy(抛弃最旧策略):即抛弃掉下一个需要执行的任务,然后将新的任务添加到队列中(4)CallerRunsPolicy(调用者策略):当队列满后,新任务将在主线程中执行,此时主线程不再接受新的请求,到达的请求将被保存在TCP层的队列中(5)设置安全策略:

Executor、ExecutorService、ThreadPoolExecutor、Executors关系

Executor是一个接口,只提供execute方法执行任务

ExecutorService也是一个接口,在继承Executor的基础上,新增了一个管理线程的方法

ThreadPoolExecutor是线程池,实现了ExecutorService中的接口,提供了创建线程池的构造函数,并且维护了4种饱和策略

Executors提供创建4种线程池,返回ExecutorService或ScheduledExecutorService

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181105G1I1QH00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券