该文章不全面,更多详情请参见:https://blog.csdn.net/qq_44750696/article/details/124651257

1. 线程池的概念:

​ 线程池就是创建一些线程,它们的集合称之为线程池。

​ 使用线程池可以很好地提高系统的性能,线程池在系统启动时,即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一个线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中,成为空闲状态,等待执行下一个任务。

2. 线程池的工作机制

​ 1)在线程池的编程模式下,系统是将任务传给整个线程池,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给这个空闲的线程。

​ 2)一个线程同时只能执行一个任务,但是可以同时向一个线程池提交多个任务。

3. 使用线程池的好处

第一:降低资源消耗。通过重复利用已创建的线程,来避免了线程的创建和销毁所造成的资源消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建,就能立即执行。

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

4. 四种线程池:

Executors:在java.util.concurrent包下面的这个Executors类中,提供了一系列的静态工厂方法,用于创建各种线程池;

其中常用的几个方法,如下:

public static ExecutorService newFixedThreadPool()
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newScheduledThreadPool()
   1、newFixedThreadPool:固定数量的线程池,该方法返回一个 可重用的、`固定线程数量`的线程池;2、newSingleThreadExecutor:单线程的线程池,它只会用`唯一的线程`来执行任务,保证所有任务按照`指定顺序(FIFO(先进先出),  LIFO(后进先出),  优先级)`执行;3、newCachedThreadPool:`可缓存线程池`,该线程池可以根据实际情况`调整池子中的线程数量`,当执行当前任务时,上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程,如果上一个线程没有结束才会新建线程,可缓存型池子通常用于执行一些生存期较短的任务;4、newScheduledThreadPool:`可定时线程池`,该线程池可以设定线程的执行时间,可以用来去执行一些`定时及周期性`的任务。

5. 线程池是如何创建的:

​ “引: JDK:(Java development toolkit),Java开发工具集 ;”

JDK提供了Executor接口(隶属于java.util.concurrent包),可以让我们有效的管理和控制我们的线程。

看源码:

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

​ 根据源码可以看出,在实际创建线程池的时候,实质调用的是ThreadPoolExecutor这个类,即,线程池执行器ThreadPoolExecutor

​ 即,在Executors类的内部,创建线程池的时候,实际新建的是一个ThreadPoolExecutor对象。

总结:说白了,线程池是通过new 一个 ThreadPoolExecutor()来创建的;

6. 线程池中是如何实现线程复用的?

1)引言,线程有两种实现方式,

​ 一种是继承Thread,重写run方法;

​ 一种是自己写一个Task实现runable接口,然后重写run方法;

启动方式如下:

//第一种方式,继承Thread,重写run方法后的启动
new MyThread().start();//第二种方式,将 `实现的runable接口的task作为参数` 传入Thread构造方法
new Thread(new Runnable() {@Overridepublic void run(){System.out.println("do something");}
}).start();

​ 我们知道,一般一个线程在执行完任务后就结束了,怎么再让他执行下一个任务呢?

​ 要想实现线程复用,必须从Runnable接口的run()方法上入手;

2)看源码:

ThreadPoolExecutor类 :

// 内部类 Worker
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{/** 各种代码...  */        Runnable firstTask;       Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;this.thread = getThreadFactory().newThread(this);}       public void run() {runWorker(this);}        }//addWorker()方法,启动线程; private boolean addWorker(Runnable firstTask, boolean core) {w = new Worker(firstTask);final Thread t = w.thread;//...代码// 很明显了,从这里启动线程的; t.start(); ...//代码}// getTask()方法,很显然这个方法是从 任务队列(workQueue)中,获取任务; private Runnable getTask() {// 各种代码...if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {//...} // ...    }// 核心部分:runWorker()方法:final void runWorker(Worker w) {// 各种代码...Runnable task = w.firstTask;w.firstTask = null;// 各种代码...try {// 重点:看这个大while循环:实现的线程复用;  while (task != null || (task = getTask()) != null) {// 各种代码...}}}

3)源码分析—在线程池中,线程是如何创建的:

​ 我们从第5节知道,线程池是通过 new 一个 ThreadPoolExecutor 这个类 来创建的;

​ 而我们的线程,是在ThreadPoolExecutor类里边的 一个名叫 Worker的内部类 里边 创建的;

​ 也就是说,只要 new Worker(Runnable firstTask),就会创建一个线程,因为Worker的构造方法里边有这么一句: this.thread = getThreadFactory().newThread(this);

​ 并且创建线程的时候,会将这个内部类本身this 传到参数列表里边,去当task。

4)源码分析—在线程池中,线程是如何启动的:

​ 在源码的addWorker()方法中,可以看到: t.start();;

​ 很明显了,从addWorker()方法这里启动线程的;

5)源码分析— 在线程池中,线程是如何运行的:

​ 从源码的addWorker()方法中,我们可以看到:w = new Worker(firstTask);

​ 也就是说,在线程启动的时候我们将内部类worker对象传入进去了,而内部类Worker是实现了runable接口、并重写了run()方法的;

​ 这也意味着:jvm会执行Worker里的run方法,使线程进入运行状态;

6)源码分析— 在线程池中,线程是如何实现复用的:

用Work内部类中的run()方法里边的runWorker()方法里边的while大循环,实现了线程的复用;

​ 在Work内部类中的run()方法里面,有一个runWorker(this)方法;(这个this指的是内部类Work对象本身)

//1,这是Work内部类中的run()方法,线程启动的时候jvm会执行它,
public void run() {runWorker(this);
}
final void runWorker(Worker w) {   // 各种代码...// 核心:这个大while循环:while (task != null || (task = getTask()) != null) {// 各种代码...}// 各种代码...
}

线程复用的核心,在这个runWorker()方法里边:

​ 从源码中可以看出,这个runWorker()方法里面,有一个大大的while循环

① 当我们的task任务,不为空的时候,即,通过getTask()方法,可以一直获取到新的任务 ,那么这个while循环就永远在进行;

​ 从而runWorker()方法不会停止,runWorker()方法外边的run()方法也就不会停止,继而线程会一直处于运行状态,去执行新的任务,从而达到了线程复用的目的

② 当我们的task任务为空了**,则while循环结束,**也就是说,这个线程结束 。

7)补充: 剖析getTask()方法:

// getTask()方法,很显然这个方法是从 任务队列(workQueue)中,获取任务;
private Runnable getTask() {// 各种代码...if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {//...} // ...
}

​ 根据getTask()这个方法的源码可以看出,getTask()方法是从任务队列中(workQueue),获取任务;

​ 这个getTask()方法里面有个三元表达式,

​ ① 当条件为真时,从任务队列(workQueue)里面,取得要执行的任务;

​ ② 当条件为假时,即任务队列没有任务了,则结束runWorker()方法里边的while大循环,从而,这个线程结束。

8) 线程复用的流程,总结

​ ①,创建一个线程池,new ThreadPoolExecutor()

​ ②,新建一个线程,在线程池中,new Worker()新建一个Worker内部类时,会新建一个线程getThreadFactory().newThread(this),并且会把这个Worker内部类本身传进去当作任务去执行,

​ ③,这个worker内部类的run()方法里的runWorker()方法,写了一个while大循环,

​ ④,当任务队列有任务时,while大循环一直进行,从而runWorker()、run()方法也就一直进行,继而该线程一直执行新的任务,达到了线程复用的目的;

​ ⑤,当任务队列没有任务时,则结束这个while循环,继而,这个线程也就结束。

线程池的相关概念以及作用相关推荐

  1. 什么是线程池以及它的作用是什么?

    概述 到目前为止我们使用多线程应用程序的目的是尽可能多地使用计算机处理器资源.所以,看起来我们仅需要为每个独立的任务分配一个不同的线程,并让处理器确定在任何时间它总会处理其中的某一个任务.额,对小系统 ...

  2. 线程池主要参数及作用

    1.线程池构造器 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUn ...

  3. java线程池的作用

    现在服务器端的应用程序几乎都采用了"线程池"技术,这主要是为了提高系统效率.因为如果服务器对应每一个请求就创建一个线程的话,在很短的一段时间内就会产生很多创建和销毁线程动作,导致服 ...

  4. java 线程作用_Java 线程池的作用

    现在服务器端的应用程序几乎都采用了"线程池"技术,这主要是为了提高系统效率.因为如果服务器对应每一个请求就创建一个线程的话,在很短的一段时间内就会产生很多创建和销毁线程动作,导致服 ...

  5. boost创建线程池_Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池

    目录 背景 线程池的来由 什么是线程池 背景总结 用法 通过Executors创建线程池 Executors及其服务的类 Executors常用的几个方法 一个线程池的例子 任务 池子 测试 说明 总 ...

  6. 面试官系统精讲Java源码及大厂真题 - 47 工作实战:Socket 结合线程池的使用

    47 工作实战:Socket 结合线程池的使用 立志是事业的大门,工作是登堂入室的旅程. --巴斯德 引导语 Socket 面试最终题一般都是让你写一个简单的客户端和服务端通信的例子,本文就带大家一起 ...

  7. 面试官系统精讲Java源码及大厂真题 - 38 线程池源码面试题

    38 线程池源码面试题 与有肝胆人共事,从无字句处读书. --周恩来 引导语 线程池在日常面试中占比很大,主要是因为线程池内容涉及的知识点较广,比如涉及到队列.线程.锁等等,所以很多面试官喜欢把线程池 ...

  8. Java线程池execute()方法源码解析

    先看作者给出的注释来理解线程池到底有什么作用 * Thread pools address two different problems: they usually * provide improve ...

  9. 24.多线程(等待唤醒机制,volatile,CAS 算法,线程池,定时器,设计模式)

    1.线程间的等待唤醒机制 Object 类中   void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待.         ...

最新文章

  1. SFB 项目经验-65-使用域管理员安装不了Exchange 2010 SP3 CU21
  2. Crash+Kdump 分析内核崩溃内存转储文件之环境搭建
  3. Java程序员从阿里、百度面试回来,这些面试题你们会吗?
  4. 推荐系统CTR预估学习路线:深度模型
  5. Fliptile (二进制压缩)
  6. 子网掩码+ip地址_C ++程序使用位掩码查找唯一编号
  7. JDK源码(10)-Integer(用处最多,重点讲解)
  8. Struts2第九篇【OGNL、valueStack详解】
  9. BIO,Socket网络编程入门代码示例,NIO网络编程入门代码示例,AIO 网络编程
  10. 陈绪:3月21日阿里云北京峰会专场出品人
  11. mysql 分表联合查询_解决分表后联合查询
  12. JsonFormat注解转换时间错误问题解决方案
  13. 使用Zend Expressive快速进行企业应用开发
  14. vscode开启鼠标滚轮缩放字体大小设置
  15. DNS域传送漏洞--vulhub复现
  16. python调用百度AI接口实现人像分割
  17. 【期权、期货及其衍生产品】学习笔记1(期权、远期)
  18. 基于JAVA的医疗机械设备管理系统设计与实现_kaic
  19. # 河源市产业发展可以这样试
  20. 邓应海:黄金虎视眈眈1800大关!最新黄金走势分析

热门文章

  1. 微信小程序云开发快速入门手册-告别切图仔的时刻到了
  2. KNN分类sklearn的make_moons数据集
  3. OutLook中发送用户密码加密的小技巧
  4. UE5项目设置必须开启光晕时,某一场景关闭光晕的方法
  5. 三菱PLC采集超声波传感器信号
  6. 堡垒机查看linux版本,Linux堡垒机实现原理(示例代码)
  7. Spring Cloud Sleuth 配置说明
  8. PostGreSql的备份和恢复
  9. Echarts实现折线图Y轴不等距百分比(最终解决方案)
  10. python如何实现下载文件_python实现下载文件的三种方法