设想在工厂里 有一个管道 客人将请求放到管道里 传递员工
工作人员读取请求单 然后去工作 没请求时候就等着
客人看不到工厂内部构造 他只看到了管道 什么都不知道
在客户端没有 工作人员这个类 客户端看不到 只能看到管道
说明管道帮助创建和启动了工作人员类
这很像threadpool
角色
channle类
还是所有的模式 都一定会有一个目标类 即操作对象 本模式是channle类 就像场景介绍一样 里面维护了创建启动工作人员类的功能 还有放请求的数组
- 由于放/取对象时需要判断条件 多线程的if 就是While 此处还要记得守护模式 就是wait notify 通信机制
request类
一个数据 维护属性 名字 编号 还有方法
clientThread类
线程类 维护的run中的业务是 调用操作类channle的放对象方法 循环创造请求 否则常见了几个线程类 就创建了几个请求
workthread类
线程类 维护的run中的业务是 调用操作类的channle的取对象方法 循环取对象 否则初始了几个线程类 就取走几个请求
优点
-
提前初始化维护线程池 循环复用线程池里的线程 不用每次都新创建线程
-
在需要工人线程跑起来的时候 调用start
-
调用与执行分离开
-
可以自己通过传参数来确定工人线程的个数
public class Channel {
private int workNums;
private WorkThread[] threadPool;
private int head;
private int tail;
private int count;
private Request[] queue;
public Channel(int workNums) {
this.workNums = workNums;
queue = new Request[workNums];
initThreadPool();
}
private void initThreadPool(){
threadPool = new WorkThread[workNums];
for (int i = 0; i < threadPool.length; i++) {
threadPool[i]=new WorkThread(i, this);
}
}
//不用新生成工作线程 直接工作
public void startWorker() {
for (WorkThread workThread : threadPool) {
workThread.start();
}
}
public synchronized void putRequest(Request request) throws InterruptedException {
while (count >= workNums) {
wait();
}
queue[tail] = request;
tail = (tail + 1) % workNums;
count++;
notifyAll();
}
public synchronized Request getRequest() throws InterruptedException {
while (count <= 0) {
wait();
}
Request res = queue[head];
head = (head + 1) % workNums;
count--;
notifyAll();
return res;
}
}
package ThreadPool;
/**
* @author sqpstart
* @create 2021-04-27 14:16
*/
public class Request {
private final String CompanyName;
private final Integer number;
public Request(String compantName, Integer number) {
CompanyName = compantName;
this.number = number;
}
@Override
public String toString() {
return "Request{" +
"CompanyName='" + CompanyName + '\'' +
", number=" + number +
'}';
}
public void execute(){
System.out.println(Thread.currentThread().getName()+"员工正在操作请求---->"+System.currentTimeMillis());
System.out.println("请求来自客人"+this.toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"操作结束");
}
}
package ThreadPool;
/**
* @author sqpstart
* @create 2021-04-27 14:25
*/
public class ClientThread extends Thread {
public ClientThread(Channel channel, String companyName) {
this.channel = channel;
this.companyName = companyName;
}
private final Channel channel;
private final String companyName;
@Override
public void run() {
try {
for (int i = 0; true; i++) {
channel.putRequest(new Request(companyName, i));
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package ThreadPool;
/**
* @author sqpstart
* @create 2021-04-27 14:51
*/
public class WorkThread extends Thread{
private final Channel channel;
public WorkThread(int workNo,Channel channel) {
this.workNo = workNo;
this.channel=channel;
}
private final int workNo;
@Override
public void run() {
//如果没有一直不停的处理请求 就会只有五次请求被处理了
while(true) {
try {
Request request = channel.getRequest();
request.execute();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
真正的线程池 --使用线程池 防止每一个请求都创建一个线程 造成资源耗尽
我们已经很详细的模拟出线程池
- 这个循环利用资源线程 不再开启线程
- 队列满了 等待
真实的线程池 就是还包括 队列满了 随时动态的新开线程 只要不打过max值
new ThreadPoolExecutor(…)
(1)、corePoolSize[5],核心线程数[一直存在,除非(allowCoreThreadTimeOut)];线程池,创建好以后就准备就绪的线程数量,就等待接收异步任务去执行
5个 Thread thread = new Thread(); thread.start();
(2)、maximumPoolSize[200]:最大线程数量;控制资源
(3)、keepAliveTime:存活时间。如果当前正在运行的线程数量大于core数量
释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
(4)、unit:时间单位
(5)、BlockingQueue workQueue:阻塞队列。如果任务有很多。就会将多的任务放在队列里面
只要有线程空闲,就会去队列里面取出新的任务继续执行
(6)、ThreadFactory:线程创建的工厂
(7)、RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略,拒绝执行
使用 Executors创建线程池 常见有四种
cachedExecutors:
可以看到他没有核心线程 自己没有主动创建维护线程 也不会维护一个线程 只要请求变少 线程有空闲 都会被清空 直到清空为0
但是却对最大线程数没有设限 说明如果遇到高并发 该线程池没有对线程创建有控制
所以可以灵活的创建和回收线程
fixedThreadPool
指定线程个数 不会增长也不会减少 都不可回收
newScheduledThreadPool
delay 可以指定多长时间以后再来执行 定时任务线程池
newSingleThreadPool 单线程线程池 只有一个线程
使用线程池的优点
降低资源消耗 重复利用已经创建好的线程避免频繁地创建和摧毁带来的资源消耗
一个CPU上运行1000个线程 看起来是并行运行1000个线程 其实是并发 时间片轮询 一个时刻 只有一个线程轮到时间片在运行 其他线程都在等待
线程创建 销毁 以及线程上下文切换还要保存现场
无论从CPU还是线程 这些都告诉我们尽量不要频繁的创建和销毁大量线程
左侧是1000个 右侧是200个线程
提高响应速度 不用新启动线程 线程池中空闲线程都是处于就绪状态的 直接就能上场
便于管理 因为都在池中 可以根据多线程的业务分池 并在资源调度的时候关闭某一个池
如下面核心业务线程池 和 非核心业务线程池