

目录
一、什么是线程池
提到线程池,我们可能想到 常量池,可以先来说说常量池:
像是字符串常量,在Java程序最初构建的时候,就已经准备好了,等程序运行的时候,这样的常量也就加载到内存中了,省下来构造/销毁的开销
计算机中,池 这个词,就只有这一个意思,表示的含义都是一样的
一个类似的例子:
海王:一个备胎不够用,我需要同时聊多个备胎,此时这多个备胎,就构成了"备胎池"。
一池子备胎,现在虽然还没用呢,随时拿出来使用,相关的池还有,
字符串常量池,线程池,进程池,内存池,数据库连接池......
线程池,就是为了让我们高效的创建销毁线程的
最大的好处就是减少每次启动,销毁线程的销毁
最初引入线程的原因:频繁创建销毁进程,太慢了
随着发展,我们对性能要求更进一步,现在觉得频繁创建销毁进程,开销有点不能接受了,解决方案有两个:
-
线程池
-
协程(纤程,轻量级线程),这个先不说
对于线程池,可以这样理解:
把线程提前创建好,放到一个地方(放到类似于数组),需要的时候,随时去取,用完还回池子中
在详细介绍线程池之前,可能会有这样的疑惑:
为什么认为,直接创建线程开销比从池子里取线程更大呢?这里大致说明一下。
一个操作系统 = 内核 + 配套的应用程序
内核包含操作系统的各种核心功能
管理硬件设备
给软件提供稳定的运行环境
一个操作系统,内核就是一份,一份内核,要给所有的应用程序技工服务支持。
- 如果有一段代码时应用程序中自行完成的,整个代码是可控的
- 如果有一段代码,需要进入内核中,由内核负责完成一系列工作,这个过程,不可控,程序员写的代码干预不了
从线程池取线程的线程,纯应用程序代码就可以完成【可控】
从操作系统创建新线程,就需要操作系统内核配合完成【不可控】
使用线程池,就可以省下应用程序切换到内核中运行这样的开销
因此,通常认为,可控的过程比不可控的过程更高效
二、线程池的详细内容
Java标准库里也提供了直接使用的线程池:ThreadPoolExecutor
线程池里准备好一些线程,让这些线程,让这些线程执行一些任务
核心方法,submit(Runnable)
通过Runnable描述一段要执行的任务
通过submit任务放到线程池里,此时线程池里的线程就会执行这样的任务
构造这个类的时候,构造方法,比较麻烦(参数比较多)

Java标准库中给了这四个构造方法,我们可以详细介绍第四个,因为这里包含前三个的所有参数。
-
int corePoolsize,核心线程数,至少有多少个线程,线程池一创建,这些线程也要随之创建,直到整个线程池销毁,这些线程才会销毁
-
int maximumPoolsize,最大线程数,核心线程+非核心线程(不繁忙就销毁,繁忙就再创建)线程也不是越多越好.
-
Long keepAliveTime,非核心线程允许空闲的最大时间,(类似于允许实习生摸鱼的时间)
-
TimeUnit unit,keepAliveTime的时间单位,是秒,分钟还是小时
-
BlockingQueue<Runnable> workQueue 工作队列
选择使用数组/链表,指定capacity,指定是否要带有优先级
线程池,本质上也就是 生产者消费者模型
调用 submit就是生产任务,线程池里的线程就是在消费任务
-
ThreadFactory threadFactory,统一的构造并初始化线程
工厂模式(也是一种设计模式,和单例模式是并列的关系),给线程类提供的工厂类,线程中有一些属性可以设置,线程池是一组线程
用来弥补构造方法的缺陷,下面的代码会报错,因为触发了方法的重载
javaclass Point{ public Point(double x, double y){ } public Point(double r, double a){ } }
构造方法的名字是固定的,要想要提供不同的版本,就需要通过重载,有时候不一定能构成重载,这时候就需要用到工厂模式。
工厂模式的核心,通过静态方法,把构造对象new的过程(各种属性初始化的过程)封装起来,提供多组静态方法,实现不同情况的构造。
javaclass Point{ public static Point makePointByXY(double x, double y){ Point p = new Point(); //通过 x 和 y 给 p 进行属性设置 return p; } public static Point makePointByRA(double r, double a){ Point p = new Point(); //通过 r 和 a 给 p 进行属性设置 return p; } }
-
RejectedExecutionHandlder handler,拒绝策略
整个线程七个参数中,最重要的,最复杂的
submit把任务添加到任务队列中,任务队列就是阻塞队列
队列满了,一般不希望程序阻塞太多
如果调用submit就阻塞(业务逻辑中的线程调用submit),就会使这个线程没法干别的事情了,不是一个好的选择,这个线程要响应用户的请求阻塞了,用户迟迟拿不到请求的回应,直观上看到的现象"卡了"
但是与其"卡了"不如直接告诉我"失败了"有四种解决方案

三、线程池的简化
Java标准库中,以提供了另一组类,针对ThreadPoolExecutor进行了进一步封装,简化线程池的使用,也是基于 工厂设计模式
java
public class demo2 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newFixedThreadPool(4);
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
int id = i;
threadPool.submit(() -> {
System.out.println("hello" + id + "," + Thread.currentThread().getName());
});
}
}
}