线程池 分手后的思念是犯贱 2023-06-07 05:40 2阅读 0赞 ## 1、什么是线程池 ## 线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。 ## 2、为什么要使用线程池 ## * 降低资源消耗 * 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 * 运用线程池能有效的控制线程最大并发数,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。 * 提高响应速度 * 当任务到达时,任务可以不需要等到线程创建就能立即执行。 ## 3、Executor 框架 ## ![format_png][] * Executor:一个接口,其定义了一个接收 Runnable 对象的方法 executor,其方法签名为 executor(Runnable command), * ExecutorService:是一个比 Executor 使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回 Future 的方法。 * AbstractExecutorService:ExecutorService 执行方法的默认实现。 * ScheduledExecutorService:一个可定时调度任务的接口。 * ScheduledThreadPoolExecutor:ScheduledExecutorService 的实现,一个可定时调度任务的线程池。 * ThreadPoolExecutor:线程池,可以通过调用 Executors 以下静态工厂方法来创建线程池并返回一个 ExecutorService 对象。 ### ThreadPoolExecutor ### `java.uitl.concurrent.ThreadPoolExecutor` 类是 Executor 框架中最核心的一个类。 ThreadPoolExecutor 有四个构造方法,前三个都是基于第四个实现。第四个构造方法定义如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ### 参数说明 ### * `corePoolSize`:线程池的基本线程数。这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了 prestartAllCoreThreads()或者 prestartCoreThread()方法,从这 2 个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为 0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中。 * `maximumPoolSize`:线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。 * `keepAliveTime`:线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。 * unit:参数 keepAliveTime 的时间单位,有 7 种取值。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。 * `workQueue`:任务队列。用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。 * ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。 * LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO (先进先出) 排序元素,吞吐量通常要高于 ArrayBlockingQueue。静态工厂方法 Executors.newFixedThreadPool()使用了这个队列。 * SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。 * PriorityBlockingQueue:一个具有优先级的无限阻塞队列。 * `threadFactory`:创建线程的工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字。 * `handler`:饱和策略。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常。以下是 JDK1.5 提供的四种策略。 * AbortPolicy:直接抛出异常。 * CallerRunsPolicy:只用调用者所在线程来运行任务。 * DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 * DiscardPolicy:不处理,丢弃掉。 * 当然也可以根据应用场景需要来实现 RejectedExecutionHandler 接口自定义策略。如记录日志或持久化不能处理的任务。 ### 重要方法 ### 在 ThreadPoolExecutor 类中有几个非常重要的方法: * `execute()` 方法实际上是 Executor 中声明的方法,在 ThreadPoolExecutor 进行了具体的实现,这个方法是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。 * `submit()` 方法是在 ExecutorService 中声明的方法,在 AbstractExecutorService 就已经有了具体的实现,在 ThreadPoolExecutor 中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和 execute()方法不同,它能够返回任务执行的结果,去看 submit()方法的实现,会发现它实际上还是调用的 execute()方法,只不过它利用了 Future 来获取任务执行结果(Future 相关内容将在下一篇讲述)。 * `shutdown()` 和 `shutdownNow()` 是用来关闭线程池的。 ### 向线程池提交任务 ### 我们可以使用 `execute` 提交任务,但是 `execute` 方法没有返回值,所以无法判断任务是否被线程池执行成功。 通过以下代码可知 `execute` 方法输入的任务是一个 Runnable 实例。 threadsPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }); 我们也可以使用 `submit` 方法来提交任务,它会返回一个 `Future` ,那么我们可以通过这个 `Future` 来判断任务是否执行成功。 通过 `Future` 的 `get` 方法来获取返回值,`get` 方法会阻塞住直到任务完成。而使用 `get(long timeout, TimeUnit unit)` 方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。 Future<Object> future = executor.submit(harReturnValuetask); try { Object s = future.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); } ### 线程池的关闭 ### 我们可以通过调用线程池的 `shutdown` 或 `shutdownNow` 方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow 首先将线程池的状态设置成 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而 shutdown 只是将线程池的状态设置成 SHUTDOWN 状态,然后中断所有没有正在执行任务的线程。 只要调用了这两个关闭方法的其中一个,isShutdown 方法就会返回 true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回 true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用 shutdown 来关闭线程池,如果任务不一定要执行完,则可以调用 shutdownNow。 参考: [https://blog.csdn.net/weixin\_41835916/article/details/81449641][https_blog.csdn.net_weixin_41835916_article_details_81449641] [https://juejin.im/post/5d1882b1f265da1ba84aa676][https_juejin.im_post_5d1882b1f265da1ba84aa676] [https://juejin.im/post/5b3cf259e51d45194e0b7204][https_juejin.im_post_5b3cf259e51d45194e0b7204] [https://zhuanlan.zhihu.com/p/62132884][https_zhuanlan.zhihu.com_p_62132884] [format_png]: /images/20230601/9c39e29917fc4b819a5bbc562047f8f3.png [https_blog.csdn.net_weixin_41835916_article_details_81449641]: https://blog.csdn.net/weixin_41835916/article/details/81449641 [https_juejin.im_post_5d1882b1f265da1ba84aa676]: https://juejin.im/post/5d1882b1f265da1ba84aa676 [https_juejin.im_post_5b3cf259e51d45194e0b7204]: https://juejin.im/post/5b3cf259e51d45194e0b7204 [https_zhuanlan.zhihu.com_p_62132884]: https://zhuanlan.zhihu.com/p/62132884
相关 Java 线程池、Runnable线程池、Callable线程池 线程池: 其实就是一个容纳多个线程的容器,其中的线程可以反复的使用,省去了频繁创建和销毁过程对象的操作,无需反复创建线程面消耗过多资源。 为什么要用线程池: 合理 青旅半醒/ 2023年02月26日 12:30/ 0 赞/ 55 阅读
相关 线程、线程池 创建线程的3种方法: package com.frank.threadPool.createThread; / @author 小石潭记 布满荆棘的人生/ 2022年10月22日 04:27/ 0 赞/ 387 阅读
相关 线程池 1.所谓线程池,就是程序的初始化阶段,就预先创建一批线程,每个线程都做好准备干活; 2.然后有一个任务列表,一开始为空,当有任务来了,就往任务列表里面添加;这个任务列表 痛定思痛。/ 2022年06月13日 13:22/ 0 赞/ 330 阅读
相关 线程池 西施越溪女,明艳光云海 最近用线程池和不用线程池做了个速度的测试,在这里备注下: 结果是速度不相上下; public static void main(Str 妖狐艹你老母/ 2022年05月20日 02:35/ 0 赞/ 284 阅读
相关 线程池 线程池 Java里面线程池的顶级接口是 java.util.concurrent.Executor , 但是严格意义上讲 Executor并不是一个线程池,而只是一个 迈不过友情╰/ 2022年03月06日 14:34/ 0 赞/ 402 阅读
相关 线程池 线程池 > 从字面义上来讲,是指管理一组同构工作线程的资源池。线程池是与工作队列密切相关的,其中在工作队列中(Worker Queue)保存了所有等待执行的任务。工作者( 清疚/ 2021年12月11日 03:35/ 0 赞/ 389 阅读
相关 线程池 可preStart一个或全部core thread 0,小于core则来一个任务建一个线程(firstTask),队列,额外线程,拒绝 一个AtomicInteger的 今天药忘吃喽~/ 2021年11月23日 03:40/ 0 赞/ 415 阅读
相关 线程池 1、先创建线程池 import java.util.concurrent.ArrayBlockingQueue; import java.util.concu 拼搏现实的明天。/ 2021年11月09日 14:28/ 0 赞/ 424 阅读
还没有评论,来说两句吧...