欢迎光临散文网 会员登陆 & 注册

Java线程池原理

2023-03-08 21:58 作者:程序员的勇敢  | 我要投稿

  线程Thread 其实是一个重量级资源,它的创建、启动以及销毁都是比较耗费资源的,而且一个系统中创建的线程数量是有限的,线程数量和系统性能其实是一个抛物线关系,当线程数量达到某个数值的时候,性能反而会下降很多,所以对于线程的管理,尤其是线程数量的控制能直接决定程序的性能。

  

  图片引用:https://blog.csdn.net/odailidong/article/details/47265123

1、线程池概念

  通过上图我们分析线程数量和系统性能是成抛物线类似的关系的,那么如何管理线程数量来达到最佳的系统性能,线程池便产生了。

  线程池:通俗的理解就是一个池子,里面存放着一些已经创建好的线程,当有任务提交到线程池执行时,池子中的某个线程会主动的执行该任务。如果池子中的线程数量不够应付数量众多的任务时,则需要自动扩充新的线程到线程池中,但是池中的线程是有最大数量限制的,当任务比较少的时候,线程池中的线程能够自动回收,释放资源。

  

 

  一个完整的线程池必须具备以下几个模块:

①、任务队列

  用于缓存提交的任务。

②、最大任务数量QueueSize

  为了防止任务过多造成的内存溢出,必须限定任务的最大数量。

③、线程池参数

  1、初始线程数量init:也就是创建线程池时预先创建好一定数量的线程。

  2、最大线程数量max:因为线程不够用时,线程池可以自动创建新的线程填充到线程池,但是不能超过线程池最大线程数量。

  3、核心线程数量core:当任务比较宽松,线程池需要释放一定的线程数量,但是最少要保证一定数量的线程数,这个线程数是核心线程数(也称为活跃线程数)。

④、任务拒绝策略

  当线程池数量已经达到最大线程数量,并且任务队列也满了,则需要相应的拒绝策略来通知任务提交者。

⑤、线程工厂

  主要用于个性化定制线程,比如将线程设置为守护线程以及设置线程名称等。

⑥、KeepAliveTime

  线程池线程数超过 core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁,终保持到 core 大小

2、线程池的作用

  ①、降低资源消耗

  通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  ②、提高响应速度

  当任务到达时,任务可以不需要等到线程创建完成就能立即使用。

  ③、提高线程的可管理性

  线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

3、JDK线程池框架

  

①、Executor

  执行器接口。

  该接口是Java线程池最上层父接口,该接口只有一个 execute 方法,作用是定义执行Runnable任务的方式

View Code

②、ExecutorService

  定义提供对Executor的服务的接口。

  该接口继承于 Executor 接口,添加了shutdown、shutdownAll、submit、invokeAll等一系列对线程的操作方法,增加Executor的行为

③、Executors

  这是一个静态工厂类,该类定义了一系列静态工厂方法,通过这些工厂方法可以返回各种不同的线程池。主要有如下四种:newCachedThreadPool、newFixedThreadPool 、newScheduledThreadPool 、newSingleThreadExecutor 。下面我们会详细介绍。

④、AbstractExecutorService

  这是一个抽象类,实现ExecuotrService接口。

⑤、ThreadPoolExecutor

  这是线程池最核心的一个类,各种线程池的实现都是基于这个类。下面是这个类的构造方法:

public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }

  定义了我们上面所说的一个线程池需要具备的各种模块,包括任务队列workQueue,核心线程池数量corePoolSize,最大线程池数量maximumPoolSize等。

 ⑥、ScheduledThreadPoolExecutor

  ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,实现了ScheduledExecutorService。在线程池的基础上,实现了可调度的线程池功能,比如指定延时执行任务,再比如周期性重复执行任务等。

 4、四种常用线程池

  前面我们讲过通过 Executors 这个静态工厂类方法能够产生各种不同的线程池,下面我们分别来介绍几种常用的线程池。

①、newCachedThreadPool

  创建一个线程池,根据需要创建新的线程,并且能够重用先前创建的线程。

  这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。

View Code

  范例:重复利用线程

View Code

  结果:

  

②、newFixedThreadPool

  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  这个实例会复用 固定数量的线程 处理一个 共享的无边界队列 。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。

  范例:控制最大并发数(最大线程数)

View Code

  结果:

  

③、newScheduledThreadPool 

  创建一个定长线程池,支持定时及周期性任务执行。

  即使线程都是空闲的,也会保持一定数量的核心线程池在里面。

1 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {2         return new ScheduledThreadPoolExecutor(corePoolSize);3     }

  范例:线程延迟3秒钟执行

View Code

④、newSingleThreadScheduledExecutor

  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  这个实例只会使用单个工作线程来执行一个无边界的队列。(注意,如果单个线程在执行过程中因为某些错误中止,新的线程会替代它执行后续线程)。它可以保证认为是按顺序执行的,任何时候都不会有多于一个的任务处于活动状态。和 newFixedThreadPool(1) 的区别在于,如果线程遇到错误中止,它是无法使用替代线程的。

  范例:单线程运行

View Code

  结果:

  

 


Java线程池原理的评论 (共 条)

分享到微博请遵守国家法律