ThreadPoolExecutor 线程池

​​​​​​​

目录

前言

1.Executor 接口介绍

2.使用 Executors 工厂类创建线程池 

1.使用 newCachedThreadPool() 方法创建无界线程池 

2.验证 newCachedThreadPool() 方法创建线程池和线程复用特性 

3.使用 newCachedThreadPool(ThreadFactory) 方法定制线程工厂

4.使用 newCachedThreadPool() 方法创建无界线程池的缺点 

5.使用 newFixedThreadPool(int) 方法创建有界线程池 

6.使用 newSingleThreadExecutor() 方法创建单一线程 

3.ThreadPoolExecutor

构造方法参数详解

总结


​​​​​​​

前言

在开发服务器端软件项目时,软件经常需要处理执行时间很短而数目巨大的请求,如果为每一个请求创建一个新的线程,则会导致性能上的瓶颈。因为JVM 需要频繁地处理线程对象的创建和销毁,如果请求的执行时间很短,则有可能花在创建和销毁线程对象上的时间大于真正执行任务的时间,所以系统性能会大幅降低。
JDK 5及以上版本提供了对线程池的支持,主要用于支持高并发的访问处理,并且复用线程对象。线程池核心原理是创建一个“线程池”(ThreadPool),在池中对线程对象进行管理包括创建与销毁,使用池时只需要执行具体的任务即可,线程对象的处理都在池中被封装了。线程池类ThreadPoolExecutor实现了 Executor 接口,该接口是学习线程池的重点,因为掌握了该接口中的方法也就大致掌握了 ThreadPolExecutor类的主要功能了


1.Executor 接口介绍

在介绍线程池之前,要先了解一下接口 java.util.concurrent.Executor,与线程池有关的大部分类都要实现此接口,该接口的声明如图所示。 

此接口的结构非常简洁,仅有一个方法,如图:

但Executor 是接口,并不能直接使用,所以还需要实现类,图中所示的内容就是完整的Executor接口相关的类继承结构。 此图相当重要,它概括了Executor 祖先接口下的全部实现类与继承关系。

ExecutorService 接口是 Executor 的子接口,在内部添加了比较多的方法,其内部结构如图所示。

虽然 ExecutorService 接口添加了若干个方法的定义,但是还是不能实例化,那么就要看它的唯一子实现类 AbstractExecutorService 类的名称来看,它是 abstract (抽象)的,查看源代码也的确是抽象类,具体如下:

public abstract class AbstractExecutorService implements ExecutorService {

所以 AbstractExecutorService 类同样是不能实例化的。 

再来看一下 AbstractExecutorService 类的子类 ThreadPoolExecutor 的源代码,具体如下:

public class ThreadPoolExecutor extends AbstractExecutorService {

通过查看代码发现,ThreadPoolExecutor 类并不是抽象的,所以可以进行实例化,进而可以使用 ThreadPoolExecutor 类的方法所提供的功能。

ThreadPoolExecutor 类的方法列表如图:

2.使用 Executors 工厂类创建线程池 

Executor 接口仅仅是一种规范、一种声明、一种定义,并没有实现任何的功能,所以大多数的情况下,需要使用接口的实现类来完成指定的功能,比如 ThreadPoolExecutor 类就是 Executor 的实现类,但 ThreadPoolExecutor 类在使用上并不是那么方便在实例化时需要传人多个参数,还要考虑线程的并发数等与线程池运行效率有关的参数,所以官方建议使用 Executors 工厂类来创建线程池对象,该类对创建 ThreadPoolExecutor 线程池进行封装,直接调用即可。

1.使用 newCachedThreadPool() 方法创建无界线程池 

使用 Executors 类的 newCachedThreadPool() 方法创建无界线程池,可以进行线程自动回收。所谓 "无界线程池" 就是池中存放线程个数是理论上的最大值,即 Interger.MAX_VALUE。 

创建测试用例,代码如下:

package org.example.Executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Executor {public static void main(String[] args) {ExecutorService service = Executors.newCachedThreadPool();service.execute(new Runnable() {@Overridepublic void run() {try {System.out.println("Runnable1 begin "+System.currentTimeMillis());Thread.sleep(1000);System.out.println("A");System.out.println("end");} catch (InterruptedException e) {e.printStackTrace();}}});service.execute(new Runnable() {@Overridepublic void run() {try {System.out.println("Runnable2 begin "+System.currentTimeMillis());Thread.sleep(1000);System.out.println("B");System.out.println("end");} catch (InterruptedException e) {e.printStackTrace();}}});}
}

运行结果如图: 

从打印的时间来看,A 和 B 几乎是在相同的时间开始打印,也就是创建了 2 个线程,而且 2 个线程之间是异步运行的。

2.验证 newCachedThreadPool() 方法创建线程池和线程复用特性 

之前的实验没有验证 newCachedThreadPool() 方法创建的是线程池,在下面的测试中进行验证。

package org.example.Executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Executors_2 {static class MyRunnable implements Runnable {private String username;public MyRunnable(String username) {this.username = username;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "username=" + username + " begin" + System.currentTimeMillis());System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());/* try {//Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}*/}}public static void main(String[] args) throws InterruptedException {ExecutorService service = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {service.execute(new MyRunnable(" " + (i + 1)));}Thread.sleep(1000);System.out.println();System.out.println();for (int i = 0; i < 5; i++) {service.execute(new MyRunnable(" " + (i + 1)));}}
}

运行结果如图:

线程复用对象了,并且可知,线程池中线程对象只有处于闲置状态时,才可以被复用。 

3.使用 newCachedThreadPool(ThreadFactory) 方法定制线程工厂

无界线程池中创建线程类的过程是可以定制的,我们可以使用 newCachedThreadPool(ThreadFactory)方法来解决这个问题。

创建测试用例,线程工厂代码如下:

    static class MyThreadFactory implements ThreadFactory{@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName("自定义线程池中线程对象的名称:"+Math.random());return thread;}}

运行 main 方法:

    public static void main(String[] args) {MyThreadFactory myThreadFactory = new MyThreadFactory();ExecutorService executorService = Executors.newCachedThreadPool(myThreadFactory);executorService.execute(new Runnable() {@Overridepublic void run() {System.out.println("我在运行" + System.currentTimeMillis() + " " + Thread.currentThread().getName());}});}

运行结果如图:

通过使用自定义的 ThreadFactory 接口实现类,实现了线程对象的定制性。 

ThreadPoolExecutor、ThreadFactory 和 Thread 之间的关系是 ThreadPoolExecutor 类使用 ThreadFactory 方法来创建 Thread 对象。

内部源代码如下:

    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory);}

在源代码中使用了默认线程工厂,源代码如下:

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

Executors.defaultThreadFactory() 方法源代码如下:

    public static ThreadFactory defaultThreadFactory() {return new DefaultThreadFactory();}

DefaultThreadFactory() 类实现关系如下:

 static class DefaultThreadFactory implements ThreadFactory

从以上源代码分析中可得知,使用无参数的 public static ExecutorService newCachedThreadPool() 方法创建线程池时,在内部隐式的使用了 DefaultThreadFactory 类。

4.使用 newCachedThreadPool() 方法创建无界线程池的缺点 

如果在高并发的情况下 ,使用 newCachedThreadPool() 方法创建无界线程池极易造成内存占用率大幅升高,导致内存溢出或者系统运行效率严重下降。

创建测试用例:

package org.example.Executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class newCachedThreadPool_No {public static void main(String[] args) {ExecutorService es = Executors.newCachedThreadPool();for (int i = 0; i < 200000; i++) {es.execute(new Runnable() {@Overridepublic void run() {try {System.out.println("runnable begin " + Thread.currentThread().getName() + " "+ System.currentTimeMillis());Thread.sleep(1000 * 60 * 5);} catch (InterruptedException e) {e.printStackTrace();}}});}}
}

程序运行后在 ”任务管理器“ 中查看 ”可用内存“ 极速下降,系统运行效率大幅度降低,超大的内存空间都被 Thread 类对象占用了,无界线程池对线程的数量并没有控制,这时可以尝试使用有界线程池来限制线程池占用的最大空间。 

5.使用 newFixedThreadPool(int) 方法创建有界线程池 

newFixedThreadPool(int) 方法创建的是有界线程池,也就是池中的线程个数可以指定最大数量。 

新建测试用例:

package org.example.Executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Exectors_3 {static class MyRunnable implements Runnable {private String username;public MyRunnable(String username) {this.username = username;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " username=" + username + " begin " + System.currentTimeMillis());Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(3);for (int i = 0; i < 6; i++) {executorService.execute(new MyRunnable("" + (i + 1)));}}
}

运行结果如图:

由图可知使用有界贤臣池之后线程池中的最多线程个数是可控的。 

6.使用 newSingleThreadExecutor() 方法创建单一线程 

使用 newSingleThreadExecutor() 方法可以创建单一线程池,单一线程池可以实现队列的方式来执行任务。

新建测试用例:

package org.example.Executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Exectors_4 {static class MyRunnable implements Runnable {private String username;public MyRunnable(String username) {this.username = username;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " username=" + username + " begin " + System.currentTimeMillis());Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {ExecutorService executorService = Executors.newSingleThreadExecutor();for (int i = 0; i < 3; i++) {executorService.execute(new MyRunnable(" " + (i + 1)));}}
}

程序运行效果如图:

3.ThreadPoolExecutor

ThreadPoolExecutor 类可以非常方便地创建线程池对象,而不需要程序员设计大量的 new 实例化 Thread 相关的代码。

前面使用 Executors 类中的 newXXXThreadExecutor() 方法可以快速地创建线程池,但创建的细节未知,因为已经被封装处理,查看 newSingleThreadExecutor() 方法时,其内部其实实例化了一个 ThreadPoolExecutor 类的实例,源代码如下:

    public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}

构造方法参数详解

ThreadPoolExecutor 类参数最全的构造方法如下:

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {

参数解释如下: 

  • corePoolSize:池中至少要保留的线程数,该属性就是定义 corePool 核心池的大小。
  • maximumPoolSize:池中允许的最大线程数,maximumPoolSize 包含 corePoolSize。
  • keepAliveTime:当线程数量大于 corePoolSize 值时,在没有超过指定的时间内是不能从线程池中将空闲的线程删除的,如果超过此时间单位,则删除空闲线程。为 0 时,执行完任务立即删除此线程。”能删除的空闲线程“范围是 maximumPoolSize ~ corePoolSize,也就是 corePoolSize 之外的线程。
  • unit:keepAliveTime 参数的时间单位。
  • workQueue:执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的任务。使用 ThreadPoolExecutor 类时一般会传入 LinkedBlockingQueue SynchronousQueue 这两个队列。
  • threadFactory:创建线程的工厂,可以是自定义的。
  • handler:是一个接口类型,用来指定当任务被拒绝执行时的处理策略。当线程池已经达到了最大线程数,且工作队列也已经满了的时候,如果还有新的任务提交到线程池,这些任务会被拒绝。在这种情况下,RejectedExecutionHandler 对象就会被调用。

注意,所谓的空闲线程就是没有执行任务的线程,不管这个线程在哪里,只要不执行任务,它就是空闲的。 

为了更好的理解这些参数在使用上的一些关系,下面对它们进行详细的讲解。

  • A 代表 execute(runnable) 要执行的 task 任务数量,如图所示。 

  • B 代表 corePoolSize,见图。
  • C 代表 maximumPoolSize ,见图。

  • D 代表 A - B(假设 A > B)的值。

构造方法中前 5 个参数之间关系较为紧密,但从使用的效果来讲,不同类型的队列能影响 ThreadPool 线程池执行的行为,所以后面的分析过程就以 LinkedBlockingQueue 和 SynchronousQueue 为主线,总结如下:

1.使用无参 new LinkedBlockingQueue() 队列的情况。

  • 注意,使用无参new LinkedBlockingQueue()队列的特点就是只使用核心池中的线程执行任务。
  1. 如果 A≤B,立即在 corePool核心池中创建线并运行任务,这些任务并不会放人LinkedBlockingQueue 中,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略。
  2. 如果 A>B && A≤C,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略,并把D放人 LinkedBlockingQueue 中等待被核心池中的线程执行。
  3. 如果 A>C,构造方法参数 maximumPoolSize、keepAliveTime 和 unit 将被忽略并把D放人  LinkedBlockingQueue 中等待被核心池中的线程执行。

2.使用 SynchronousQueue 队列的情况。

  1. 如果 A≤B,立即在 corePool 核心池中创建线程并运行任务,这些任务并不放人SynchronousQueue 中,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略。
  2. 如果 A>B&& A≤C,则构造方法参数 maximumPoolSize、keepAliveTime和unit有效,并且马上创建最多C个线程运行这些任务,而不把D放入 SynchronousQueue 队列中,D执行完任务后在指定 keepAliveTime 时间发生超时时,将D进行清除,如果D在keepAliveTime时间之后未完成任务,则在D完成任务后进行清除。
  3. 如果 A>C,则最多处理 C个任务,其他任务(不包括核心池中的任务)不再处理并抛出异常。

3.使用new LinkedBlockingQueue(xxxxx) 队列有参的情况。其中,参数xxxxx代表队列的最大存储长度。

  • 注意,使用有参 new LinkedBlockingQucue(xxxxx) 队列的执行特点是心池中的线程和maximumPoolSize-corePoolSize 线有可能一起执行任务,也就是多执行任务的线程数量就是 maximumPoolSize。另外在使用有参new LinkedBlockingQueue(xxxx)队列时,执行的流程是先判断 corePoolSize 大小够不够,如果不够则向 new LinkedBlockingOueue(xxxx)刚中存储,如果 new LinkedBlockingQueue(xxxxx)队列中放不下,则将剩余的任务尝试向 C-B 中存放,如果 C-B 放不下就报异常。
  1. 如果 A≤B,立即在 corePool 核心池中创建线程并运行任务,这些任务并不放入new LinkedBlockingQueue(xxxxx)队列中,构造方法参数 maximumPoolSize、keepAliveTime和unit 将被忽略。
  2. 如果 A>B 且 (A-B) ≤ xxxxx,立即在 corePool 核心池中创建线程并运行任务,构浩方法参数 maximumPoolSize keepAliveTime 和 unit 被忽略,并把 (A-B) 放人LinkedBlockingOueue(xxxxx)队列中等待被核心池中的线程执行。
  3. 如果A>B、(A-B)>xxxxx,并且 (A-B-xxxxx)<(C-B),立即在corePool核心池中创建线程并运行任务,构造方法参数 maximumPoolSize、keepAliveTime和unit有效,并且马上创建 (A-B-xxxxx) 个线程运行这些任务,(A-B-xxxxx)执行完任务后在指定 keepAliveTime 时间发生超时时,将(A-B-xxxxx)进行清除,如果 (A-B-xxxxx) 在keepAliveTime时间之后未完成任务,则在(A-B-xxxxx)完成任务后进行清除。
  4. 如果A>B、(A-B)>xxxxx,并且(A-B-xxxxx)>(C-B),立即在corePool核心池中创建线程并运行任务,构造方法参数 maximumPoolSize、keepAliveTime和unit有效,马上创建(C-B)个线程运行这些任务,(C-B)个任务执行完任务后在指定 keepAliveTime时间发生超时时,将(C-B)进行清除,如果(C-B)在 keepAliveTime 时间之后未完成任务,则在(C-B)完成任务后进行清除,(A-B-xxxxx)-(C-B)多出来的任务被拒绝执行并出现异常。 

1.构造方法前 2 个参数与 getCorePoolSize() 和 getMaximumPoolSize() 方法 

新建测试用例:

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutor_1 {//获取基本属性 corePoolSize 和 maximumPoolSizepublic static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());System.out.println(executor.getCorePoolSize());System.out.println(executor.getMaximumPoolSize());System.out.println("");executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new SynchronousQueue<>());System.out.println(executor.getCorePoolSize());System.out.println(executor.getMaximumPoolSize());}
}

从代码中可以分析出,线程池中保存的 core 线程数是7,最大为8,运行结果如图。 

2.验证 (1.1)和(2.1)的情况

(1.1)测试用例:

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Run2_1 {// 线程数量小于等于 corePoolSize// keepAliveTime 大于 5 时也不清除空闲线程// 因为空闲线程在 corePool 中// corePool 中的线程是不能被删除的// 所以 keepAliveTime 参数无效public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4executor.execute(runnable);//5executor.execute(runnable);//6executor.execute(runnable);//7Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 10s 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());}//按钮呈红色,因为池中还有线程在等待任务。
}

运行结果如图,由于线程数量小于等于 7,打印信息 ”B executor.getCorePoolSize()=7“说明核心线程超过 5 s,不清除。7个线程对象成功运行,说明线程池成功工作了,符合(1.1)情况:

测试(2.1)只需要更改使用队列为 SynchronousQueue 结果与上方并无差别,说明只要线程数量小于等于 corePoolSize 就不清除空闲线程,而且和使用队列无关。

3.验证(1.2)的情况 

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Run2_1 {// 队列使用 LinkedBlockingQueue,也就是如果// 线程数量大于 corePoolSize 并且小于等于 maximumPoolSize时将maximumPoolSize-corePoolSize的任务放入队列中// 同一时间最多只能有 7 个线程在运行// 如果使用 LinkedBlockingQueue 类则 maximumPoolSize 参数作用将忽略// 因为 任务都放入 LinkedBlockingQueue 队列中了public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4executor.execute(runnable);//5executor.execute(runnable);//6executor.execute(runnable);//7executor.execute(runnable);//8Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 10s 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());}//按钮呈红色,因为池中还有线程在等待任务。// LinkedBlockingQueue abc;
}

8 个线程成功运行,从结果来看,完全符合(1.2)的情况。BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于可以没有大小限制,有点是队列容量非常大,而线程池中运行的线程数也永远不会超过 corePoolSize 值,因为其他多余的线程被放入 LinkedBlockingQueue 中,keepAliveTime 参数也就没有意义了。

4.验证 (2.2) 的情况

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Run2_1 {// 队列使用 SynchronousQueue,也就是如果// 线程数量大于 corePoolSize 时将任务放入池中,总数量为 8// 没有超过 maximumPoolSize 值// 由于运行的线程数为 8 ,因此数量上大于corePoolSize为7的值//所以 keepAliveTime > 5 时清除空闲线程public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{long ok = System.currentTimeMillis();System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(15000);System.out.println("    endTime="+(System.currentTimeMillis()-ok));} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,7, TimeUnit.SECONDS,new SynchronousQueue<>());executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4executor.execute(runnable);//5executor.execute(runnable);//6executor.execute(runnable);//7executor.execute(runnable);//8Thread.sleep(1000);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 10s 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 20s 后打印结果。");System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("C executor.getPoolSize()="+executor.getPoolSize());System.out.println("C executor.getQueue().size()="+executor.getQueue().size());}//按钮呈红色,因为池中还有线程在等待任务。// LinkedBlockingQueue abc;
}

符合(2.2)的情况,同时”如果D在keepAliveTime时间之后未完成任务,则在D完成任务后进行清除“测试成功,执行任务后空闲时间一过立即从线程池中被清除。

5.验证(1.3)的情况 

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Run4_1 {//队列使用 LinkedBlockingQueue类//并且线程数量大于 corePoolSize 时将其余的任务放入队列中//同一时间最多只能有 7 个线程在运行//所以 keepAliveTime 大于 5 时也不清除空闲线程public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4executor.execute(runnable);//5executor.execute(runnable);//6executor.execute(runnable);//7executor.execute(runnable);//8executor.execute(runnable);//9Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 10s 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());}//通过此实验可以得知,如果使用 LinkedBlockingQueue 作为任务队列。//则不管线程数大于 corePoolSize 还是大于 maximumPoolSize//都将多余的任务放入队列中//程序都可以正确无误的运行,并且不会出现异常//此时按钮呈红色,因为池中还有线程在等待任务。
}

程序运行结果如下,符合(1.3)的情况:

6.验证(2.3) 的情况

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Run4_1 {//队列使用 SynchronousQueue//并且线程数量大于 corePoolSize//并且线程数量大于 maximumPoolSize//所以出现异常public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new SynchronousQueue<>());executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4executor.execute(runnable);//5executor.execute(runnable);//6executor.execute(runnable);//7executor.execute(runnable);//8executor.execute(runnable);//9Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);System.out.println(" 10s 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());}}

运行了 8 个任务,其他的任务都没有运行,从结果来看,符合(2.3)情况。

7.验证 (3.2)的情况

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class RunX_1 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(800);System.out.println(" 800 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(1000);System.out.println(" 1000 后打印结果。");System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("C executor.getPoolSize()="+executor.getPoolSize());System.out.println("C executor.getQueue().size()="+executor.getQueue().size());}}

运行结果如图,完全符合(3.2)的情况:

8.验证(3.3)的情况

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class RunX_1 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));//使用 core 队列中的线程executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3//使用第三方队列中的线程executor.execute(runnable);//1executor.execute(runnable);//2// C-Bexecutor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(800);System.out.println(" 800 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(1000);System.out.println(" 1000 后打印结果。");System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("C executor.getPoolSize()="+executor.getPoolSize());System.out.println("C executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);    //下面打印是验证销毁了 C-BSystem.out.println(" 10000 后打印结果。");System.out.println("D executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("D executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("D executor.getPoolSize()="+executor.getPoolSize());System.out.println("D executor.getQueue().size()="+executor.getQueue().size());}}

运行结果如图:

9.验证(3.4)的情况 

package org.example.Executor;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class RunX_1 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {@Overridepublic void run() {try{System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));//使用 core 队列中的线程executor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3//使用第三方队列中的线程executor.execute(runnable);//1executor.execute(runnable);//2// C-Bexecutor.execute(runnable);//1executor.execute(runnable);//2executor.execute(runnable);//3executor.execute(runnable);//4Thread.sleep(300);//可以看成 车中可载人的标准人数System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());//车中可载人的最大人数System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());//车中正在载的人数System.out.println("A executor.getPoolSize()="+executor.getPoolSize());//站在地面上等待被送的人数System.out.println("A executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(800);System.out.println(" 800 后打印结果。");System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("B executor.getPoolSize()="+executor.getPoolSize());System.out.println("B executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(1000);System.out.println(" 1000 后打印结果。");System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("C executor.getPoolSize()="+executor.getPoolSize());System.out.println("C executor.getQueue().size()="+executor.getQueue().size());Thread.sleep(10000);    //下面打印是验证销毁了 C-BSystem.out.println(" 10000 后打印结果。");System.out.println("D executor.getCorePoolSize()="+executor.getCorePoolSize());System.out.println("D executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());System.out.println("D executor.getPoolSize()="+executor.getPoolSize());System.out.println("D executor.getQueue().size()="+executor.getQueue().size());}}

运行结果如图,符合(3.4)的情况:

10.线程执行流程分析 

前面已经介绍过线程池在执行任务时的流程分析,在使用有参 new LinkedBlockingQueue(xxxx) 队列时,执行的流程时先判断 corePoolSize 大小够不够,如果不够向 new LinkedBlockingQueue(xxxx) 队列中存储,如果 new LinkedBlockingQueue(xxxx) 队列中放不下,则将剩余得到任务尝试向 C-B 中存放,如果 C-B 放不下就报异常。 


总结

通过使用线程池,我们更加知道了如何更加合理地分配线程资源,提高性能瓶颈。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/265871.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信商家收款码扣多少手续费

很多人想申请低手续费率的收款码不知从何下手&#xff0c;在参考了大量博客教学之后&#xff0c;终于搞懂了详细流程以及注意事项。在此记录一下。我申请的是一个只需要0.2%费率的微信收款码&#xff0c;申请时间是2022年2月12日。申请之前只需要准备营业执照和法人身份z&#…

0011Java安卓程序设计-ssm基于移动端的家庭客栈管理系统

文章目录 **摘** **要**目 录系统实现5.1小程序端5.2管理员功能模块开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;776871563 摘 要 网络的广泛应用给生活带来了十分的便利。所以把家庭客栈管理与现在网络相结合&#xff0c;利用java…

HttpComponents: 概述

文章目录 1. 概述2. 生态位 1. 概述 早期的Java想要实现HTTP客户端需要借助URL/URLConnection或者自己手动从Socket开始编码&#xff0c;需要处理大量HTTP协议的具体细节&#xff0c;不但繁琐还容易出错。 Apache Commons HttpClient的诞生就是为了解决这个问题&#xff0c;它…

用PHP和HTML做登录注册操作数据库Mysql

用PHP和HTML做登录注册操作数据库Mysql 两个HTML页面&#xff0c;两个PHP,两个css,两张图片&#xff0c;源码资源在上方。 目录 HTML页面 login.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta nam…

初识人工智能,一文读懂强化学习的知识文集(5)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

基于SSM的酒店管理旅店系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

基于vue开发 - 编写登录页面样式

vue创建项目&#xff0c;使用可视化界面安装插件-CSDN博客 使用vue UI安装路由插件-CSDN博客 基于vue开发-创建登录页-CSDN博客 在src/views文件夹中创建登录页面login.vue&#xff0c;在router/index.js文件中加入登录页的路由&#xff0c;然后在浏览器中输入登录页的路径就…

java--StringBuilder、StringBuffer、StringJoiner

1.StringBuilder ①StringBuilder代表可变字符串对象&#xff0c;相当于是一个容器&#xff0c;它里面装的字符串是可以改变的&#xff0c;就是用来操作字符串的。 ②好处&#xff1a;StringBuilder比String更适合做字符串的修改操作&#xff0c;效率会比更高&#xff0c;代码…

中文读唇总动员:CNVSRC 2023 研讨会圆满落幕

12月9日晚&#xff0c;NCMMSC-CNVSRC 2023 学术研讨会在苏州召开的第十八届全国人机语音通讯学术会议&#xff08;NCMMSC 2023&#xff09;举办&#xff0c;会上公布了本次视觉语音识别竞赛 CNVSRC 2023 的最终结果&#xff0c;并举行颁奖仪式。 本次竞赛由 NCMMSC 2023组委会…

【C知道】帮我答疑解惑:java的entity字段是map,如何映射到数据库

目录 一、问题场景描述 二、跟【C知道】第一次沟通 &#xff08;1&#xff09;我问 &#xff08;2&#xff09;他答 &#xff08;3&#xff09;我说 三、跟【C知道】第二次沟通 &#xff08;1&#xff09;我问 &#xff08;2&#xff09;他答 &#xff08;3&#xff0…

大厂外包干了2个月,技术明显进步了...

先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年8月份&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

实现加盐加密方法以及MappedByteBuffer,RandomAccess

目录 自己实现 Spring Security MappedByteBuffer RandomAccess 加盐加密的实现 自己实现 传统MD5可通过彩虹表暴力破解&#xff0c; 加盐加密算法是一种常用的密码保护方法&#xff0c;它将一个随机字符串&#xff08;盐&#xff09;添加到原始密码中&#xff0c;然后再进…