重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章将为大家详细讲解有关java的线程池原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
成都创新互联始终坚持【策划先行,效果至上】的经营理念,通过多达十载累计超上千家客户的网站建设总结了一套系统有效的推广解决方案,现已广泛运用于各行各业的客户,其中包括:三轮搅拌车等企业,备受客户赞美。
在之前已经使用过线程池了。在使用中,基本上就是初始化好线程池的实例之后,把任务丢进去,等待调度执行就可以了。使用起来非常简单方便。
每次new Thread新建对象,性能差
线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM
缺少更多功能,如更多执行、定期执行、线程中断
重用存在的线程,减少对象创建、消亡的开销,性能好
可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
提供定时执行、定期执行、单线程、并发数控制等高级功能
参数:
corePoolSize:核心线程数量。建议和cpu的核心数差不多,当有任务提交,检测当前线程池内的线程数小于corePoolSize的话,新建线程执行任务,而不会开始复用,会新建,直到达到corePoolSize。线程池内的线程数大于等于corePoolSize时,将任务放入workQueue等待。
maximumPoolSize:允许线程池内最大线程数。当队列满了之后,如果线程池内的线程数小于maximumPoolSize则新建线程,如果大于等于执行拒绝策略。
如果maximumPoolSize是30,corePoolSize是10,当队列满了后只能再开20个线程。
workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响
keepAliveTime:线程没有任务执行时最多保持多久时间终止。线程池维护线程所允许的空闲时间。当线程池中的线程数量大于CorePoolSize时,如果这是没有新的任务提交,线程会等待,直到时间超过keepAliveTime才销毁。
unit:keepAliveTime的时间单位
threadFactory:线程工厂,用来创建线程,会有一个默认的工厂来创建线程。使用默认的工厂创建线程时,线程拥有相同的优先级,并且是非守护的线程,同时也设置了线程的名称。
rejectHandle:当拒绝处理任务时的策略。如果workQueue阻塞队列满了,并且没有空闲的线程时,这时还继续提交任务,我们就需要一种策略来处理这个任务。线程池总共提供了四种策略:
直接抛出异常,默认策略
用调用者所在的线程执行任务
丢弃队列中最靠前的任务,并执行当前任务
直接丢弃这个任务
如果运行的线程数小于CorePoolSize时,直接创建新线程创建任务,即使线程池中的其他线程是空闲的。
如果线程池中的线程数量大于等于CorePoolSize且小于maximumPoolSize时,则只有当wokQueue满时才创建新的线程去处理任务。
如果我们设置CorePoolSize与maximumPoolSize相等,那么创建的线程池大小是固定的,这时如果有新任务提交且workQueue还没满,就把请求放入workQueue中,等待空闲线程从workQueue中取任务进行处理。
如果运行的线程数量大于maximumPoolSize时,这时如果workQueue满,那么会通过一个拒绝策略参数来指定策略处理任务。
如果我们想降低系统资源的消耗,包括CPU的使用率、操作系统资源的消耗,可以设置一个较大的workQueue容量和较小的CorePoolSize容量,这样会降低线程处理的吞吐量。如果我们提交的任务经常发生阻塞,我们可以设置maximumPoolSize来设置线程池容量。如果我们队列容量较小,通常需要把maximumPoolSize设置大一些,这样CPU使用率会高一些。但是如果线程池容量设置过大,在提交人物数量过多的情况下,并发量会增加,那么线程间资源调度就是一个需要考虑的问题,反而会降低处理的吞吐量。
当我们初始化一个线程池之后,通常有上面几种状态。
running:可以接受新提交的任务,也能处理阻塞队列中的任务。
shutdown:关闭状态,当一个线程池实例处于shutdown状态时,不能再接收新提交的任务,但却可以继续处理阻塞队列中已经保存的任务。
stop:也不能接收新的任务,也不处理队列中的任务。会中断正在处理的线程任务。
tidying:所有任务都已经终止了,没有活动中的线程。当线程池进行该状态时候,会执行钩子方法terminated() 。
execute():提交任务,交给线程池执行
submit():提交任务,能够返回执行结果 相当于execute+Future
shutdown():关闭线程池,等待任务都执行完
shutdownNow():关闭线程池,不等待任务执行完
getTaskCount():线程池已执行和未执行的任务总数
getCompletedTaskCount():已完成的任务数量
getPoolSize():线程池当前的线程数量
getActiveCount():当前线程池中正在执行任务的线程数量
Executors.newCachedThreadPool:创建一个可缓存的线程池,如果线程池的长度超过了线程的需要,可以灵活回收空闲线程,如果没有回收的,可以新建线程
Executors.newFixedThreadPool:创建一个定长的线程池,可以控制线程的最大并发数,超出的线程会在队列中等待
Executors.newScheduledThreadPool:也是创建一个定长的线程池。支持定时以及周期性的任务执行。
Executors.newSingleThreadExecutor:创建一个单线程的线程池,会用唯一的工作线程来执行任务。保证所有任务按照指定顺序去执行(先入先出等)。
Executors.newCachedThreadPool示例代码:
@Slf4j public class ThreadPoolExample1 { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i< 10; i++){ final int index = i; executorService.execute(new Runnable() { @Override public void run() { log.info("task:{}", index); } }); } //一定要关闭线程池,否则程序不结束 executorService.shutdown(); } }
Executors.newSingleThreadExecutor示例代码:
@Slf4j public class ThreadPoolExample3 { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i< 10; i++){ final int index = i; executorService.execute(new Runnable() { @Override public void run() { log.info("task:{}", index); } }); } //一定要关闭线程池,否则程序不结束 executorService.shutdown(); } }
输出结果:
17:08:16.870 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:0 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:1 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:2 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:3 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:4 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:5 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:6 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:7 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:8 17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:9
相当于单线程按顺序执行。
Executors.newScheduledThreadPool代码示例:
@Slf4j public class ThreadPoolExample4 { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { log.warn("schedule run"); } },1, 3, TimeUnit.SECONDS); //这里不需要关闭线程池 //scheduledExecutorService.shutdown(); } }
上面目的是:延迟1秒后,每个三秒执行一次任务,由于这个任务时不定的执行,因此这里不应该关闭线程池。如果需要关闭线程池的话,可以设置一个触发条件来关闭。类似于Timer类:
@Slf4j public class ThreadPoolExample4 { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { log.warn("timer run"); } }, new Date(), 5*1000); } }
CPU密集型任务,就需要尽量压榨CPU,参考值可以设置为NCPU+1
如果是IO密集型任务,参考值可以设置为2*NCPU
我们只用线程池主要是为了重用存在的线程,减少对象创建消亡,能有效控制最大线程并发数,可以避免过多的资源竞争和阻塞,也可以定时执行单线程与控制线程的执行性能比较好。 这不代表线程池应该随时随地用,一定要根据自己的实际场景来分析使用参数配置
关于java的线程池原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。