(四)java 线程,线程池的使用

为什么要使用线程池?

   创建线程是简单的,但启动后的线程犹如脱缰野马,难于管理,特别是多线程使用场景,线程之间的互相竞争,可能使 cpu 花费更多时间在各个线程之间切换,而且线程结束后的回收由垃圾回收控制,你不知道工作结束的线程还会存活多久,是否持有着什么资源。而且线程对象提供使用方法有限,无法提供定时启动、线程并发数控制等操作,所以,线程池出现了。
   线程池提供了对线程的管理,让线程稍微能得到控制(中断、取消等);并发线程数的控制,提高系统资源使用率,避免过多的资源竞争;复用线程,减少线程对象创建,提高线程启动效率以及降低系统资源消耗;提供延时启动、定期执行等操作。

线程池介绍

具体线程池有以下几个:

  • ThreadPoolExecutor
    继承 AbstractExecutorServiceExecutors 提供的默认线程池,除了定时线程池,其他都是ThreadPoolExecutor的默认参数构建
  • ScheduledThreadPoolExecutor可调度线程池(定时线程池)
    继承 ThreadPoolExecutor实现ScheduledExecutorService
  • ForkJoinPool ,继承 AbstractExecutorService
    特殊线程池,分治法(Divide-and-Conquer Algorithm),可以将一个大任务(线程)在细分多个小任务执行,直到所有小任务完成才算大任务完成,充分利用多核 cpu 的特性。与 ThreadPoolExecutor 区别,ForkJoinPool可以实现在有限的线程数下完成非常多的任务,如使用4个线程完成具有父子关系的400万个任务,而 ThreadPoolExecutor 无法选择优先执行子任务,所以处理400万的任务,需要400万个线程。

  • ExecutorCompletionService
    实现 CompletionService,构造方法传入 Executor,可以认为是对 Executor 的使用封装。

使用较多的线程池是 ThreadPoolExecutor,本文只介绍这个。对其他类型有兴趣的可以去 java.util.concurrent 中查看。

构造方法:

public ThreadPoolExecutor(int corePoolSize,//核心池数量,并发的线程数
                          int maximumPoolSize,//线程池最大容量
                          long keepAliveTime,//线程存活时间
                          TimeUnit unit,//时间单位
                          BlockingQueue<Runnable> workQueue,//线程缓存队列
                          ThreadFactory threadFactory,//线程创建工厂
                          RejectedExecutionHandler handler//加入线程失败策略) {
                              //... 省略

}

向线程池提交一个任务,如果核心池未满,则用该命令作为第一个任务开启一个新线程

if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))//true:添加成功 false:添加失败
        return;
    c = ctl.get();
}

如果核心池已经满了,检查线程池最大容量,没有超过,则加入缓存工作队列,创建新线程去执行任务;当线程池达到最大容量,并且缓存队列已经饱和时,此时再继续往线程池中添加任务,则会拒绝。
由缓存队列创建的线程,如果处于空闲状态,在一定的时间(keepAliveTime)后将会被回收,而核心池中的线程如果 allowCoreThreadTimeOut 该参数为 false(默认值) 即使处于空闲状态也不会被回收。

流程图如下:

Created with Raphaël 2.1.2 添加任务 核心池已满? 加入工作队列 达到最大线程数? 超出工作队列容量? 移出工作队列 拒绝 结束 添加成功 创建线程 yes no yes no yes no

使用:

ThreadPoolExecutor pool = new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
pool.execute(()-> print "runnable");

一般来说,除非有特殊的处理要求,否则建议使用Executors 中的方法构造线程池,对于线程池的核心数、最大容量、工作队列结构、线程工厂等设置,默认构造基本满足开发需求。

关闭线程池:

//不再接受新任务,直到缓存区任务执行完毕
public void shutdown(){...}
//不再接受新任务,将缓存区任务返回,并清空工作区,中断运行中的任务
public List<Runnable> shutdownNow() {...}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池是一种用于管理和复用线程的机制。它可以在需要执行任务时,从线程池中获取可用的线程来执行任务,而不是每次都创建新的线程。这样可以减少线程创建和销毁的开销,提高系统的性能和资源利用率。 Java线程池通常由一个线程池管理器、工作队列和一组工作线程组成。线程池管理器负责创建、初始化和释放线程池,工作队列用于存储待执行的任务,工作线程则负责从队列中取出任务并执行。 使用线程池可以带来以下好处: 1. 提高系统性能:通过复用线程,降低了线程创建和销毁的开销,提高了系统的响应速度。 2. 控制资源消耗:通过限制线程的数量,可以控制系统同时运行的线程数量,避免资源过度消耗。 3. 提高系统稳定性:通过合理配置线程池参数,可以避免因为系统资源耗尽而导致系统崩溃。 在Java中,可以使用java.util.concurrent包中的Executor框架来创建和管理线程池。常用的线程池类型有FixedThreadPool、CachedThreadPool、SingleThreadExecutor等,每种类型适用于不同的场景和需求。 要使用线程池,通常需要以下步骤: 1. 创建线程池对象,并指定线程池的类型和参数。 2. 创建任务(Runnable或Callable对象)并提交给线程池。 3. 线程池会根据任务的类型和当前线程池状态,决定是创建新线程、复用现有线程,还是将任务加入到工作队列中。 4. 线程池中的工作线程会不断从队列中获取任务,并执行任务的run方法。 5. 当不再需要执行任务时,需要关闭线程池,释放资源。 通过合理使用线程池,可以有效管理线程,提高系统的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值