JAVA多线程(5)

JAVA多线程(5)

线程池

原理

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。他的主要特点为:线程复用;控制最大并发数;管理线程。
重用存在的线程,减少对象创建销毁的开销,且提高了响应速度;有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,且可以定时定期执行、单线程、并发数控制,配置任务过多任务后的拒绝策略等功能。

线程池类别

  • newFixedThreadPool :一个定长线程池,可控制线程最大并发数。
  • newCachedThreadPool:一个可缓存线程池。
  • newSingleThreadExecutor:一个单线程化的线程池,用唯一的工作线程来执行任务。
  • newScheduledThreadPool:一个定长线程池,支持定时/周期性任务执行。
  • 线程池尽量不要使用 Executors 去创建,而是通过 ThreadPoolExecutor的方式去创建,因为Executors创建的线程池底层也是调用 ThreadPoolExecutor,只不过使用不同的参数、队列、拒绝策略等如果使用不当,会造成资源耗尽问题。直接使用ThreadPoolExecutor让使用者更加清楚线程池允许规则,常见参数的使用,避免风险。

主要参数

  • corePoolSize:核心线程数,默认情况下创建的线程数,默认情况下核心线程会一直存活,是一个常驻线程。
  • maximumPoolSize:线程池维护线程的最大数量,超过将被阻塞!(当核心线程满,且阻塞队列也满时,才会判断当前线程数是否小于最大线程数,才决定是否创建新线程)
  • keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收,直到线程数量等于 corePoolSize。
  • unit:指定 keepAliveTime 的单位,如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS
  • workQueue:线程池中的任务队列,常用的是 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。
  • threadFactory:创建新线程时使用的工厂
  • handler:RejectedExecutionHandler 是一个接口且只有一个方法,线程池中的数量大于 maximumPoolSize,对拒绝任务的处理策略,默认有 4 种策略:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

四种拒绝策略

  • AbortPolicy:中止策略。默认的拒绝策略,直接抛出 RejectedExecutionException。调用者可以捕获这个异常,然后根据需求编写自己的处理代码。
  • DiscardPolicy:抛弃策略。什么都不做,直接抛弃被拒绝的任务。
  • DiscardOldestPolicy:抛弃最老策略。抛弃阻塞队列中最老的任务,相当于就是队列中下一个将要被执行的任务,然后重新提交被拒绝的任务。如果阻塞队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务,因此最好不要将该策略和优先级队列放在一起使用。
  • CallerRunsPolicy:调用者运行策略。在调用者线程中执行该任务。该策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者(调用线程池执行任务的主线程),由于执行任务需要一定时间,因此主线程至少在一段时间内不能提交任务,从而使得线程池有时间来处理完正在执行的任务。

线程池中线程复用的原理

  • 线程池将线程和任务讲行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread创建线程时的个线程必须对应一个任务的限制。
  • 在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread进行了封装,并不是每次执行任务都会调用 Thread. start()来创建新线程,而是让每个线程去执行一个循环任务,在这个循环任务中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run方法串联起来。

如果你提交任务时,线程池队列已满,这时会发生什么

  • 如果使用的是无界队列,没关系,继续添加任务到阻塞队列中等待执行
  • 如果使用的是有界队列,任务首先会被添加到队列中,如果队列满了,会根据maximumPoolSize的值增加线程数量,如果增加了线程数量还是处理不过来,队列依然是满的,那么则会使用拒绝策略处理满了的任务,默认是 AbortPolicy。

阻塞队列的作用

  • —般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前要继续入队的任务。
  • 阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放CPU资源。
  • 阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活、不至于一直占用cpu资源。
  • 为什么先添加队列而不是先创建最大线程?
    • 在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影晌了整体效率。 就好比一个企业甲面有10个(core)正式工的名额,最多招10个正式工,要是任务超过正式工人数(task>core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超过正式工的加班忍耐极限了(队列满了),就的招外包帮忙了(注意是临时工)要是正式工加上外包还是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值