目录
1.线程池基础知识
1.1线程池概念
1.2线程池使程序更高效的原理
2.标准库中的线程池
2.1线程池的继承关系
2.2线程池的构造方法
2.3线程池的使用
2.线程池的优点
3.线程池的模拟实现
在多线程当中,并发程度会不断提升,随之带来的是线程频繁的创建和销毁,此时程序的开销就会变大,为了改善这个现象,就引入了线程池,程序将创建好的线程放入到“线程池”中,当程序需要用时,可以直接在这个“池子”中取出来,线程用完之后再放入到“池子”中。这样在就不会再频繁的创建和销毁了,可以使程序开销减小,效率更高。
在创建、销毁线程时我们时是交给操作系统内核来完成的, 而我们使用线程池调度是以用户态实现的。
如果将任务交给操作系统的内核去完成,它的完成时间是不可控的,这是为什么呢?原因在于:内核需要对多个任务提供服务,在你交给它任务时,它可能无法仅仅单一的完成你交与的任务,可能还需要完成别人交与它的任务。
而用户态就不一样了,用户态就仅会对自己的任务负责,因此效率更高,开销更小。
下面这个例子可以让你更加容易理解(银行办事):
有一天你去银行办理业务,排队排了好久,工作人员问你:拿身份证复印件了吗?你心想:卧槽还要这个。你回答:没有拿。然后工作人员说,没关系,你有两个选择:
1.我给你打印然后办理。(相当于内核态)
2.你自己打印然后给我。 (相当于用户态)
选择1:当让工作人员打印,可能他手头上出了你的打印东西在这个事还有其他的事,因此让他帮打印的话,打印好就不知道事猴年马月了。
选择2: 我自己直接去大厅的复印机这里,直接就打印好了,然后拿给工作人员,办完就跑。
很明显,在1、2选择中,选择2更高效,这也就是为什么线程池更高效了。
Java中提供了线程池的标准类(ThreadPoolExecutor),构造方法(常用的一个)如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
对构造方法中参数解读
1. corePoolSize(核心线程);相对于公司的正式员工,签了劳动合同的,不能够随意开除。
2.maximumPoolSize (最大线程数):线程最多不能超过这这个数,最大线程数=核心线程+非核心线程;非核心线程就相当于公司招来的实现生。
3.keepAliveTime(非核心线程的存活时间):如果找来的实习生一直摸鱼没有活干,超过这个时间就将他炒鱿鱼。
4.unit:上方存活时间的单位
5.workQueue(用于缓存未执行的任务):线程的任务队列,通过submit方法加入的任务进入这个队列
6.threadFactory(线程工厂):线程的创建方案。
7.handler(拒绝策略):当任务对象线程满了的话,应该如何做出回答。
在Java总handler有四种拒绝策略:
Modifier and Type | Class and Description |
---|---|
static class |
ThreadPoolExecutor.AbortPolicy 被拒绝的任务的处理程序,抛出一个 |
static class |
ThreadPoolExecutor.CallerRunsPolicy 一个被拒绝的任务的处理程序,直接在 |
static class |
ThreadPoolExecutor.DiscardOldestPolicy 被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试 |
static class |
ThreadPoolExecutor.DiscardPolicy 被拒绝的任务的处理程序静默地丢弃被拒绝的任务。 |
线程池在实现时,我们需要用到工具类Executors,调用里面的静态方法来完成创建的,他的所有方法的返回值都是ExecutorService。
import java.util.concurrent.*;
public class testDemo {
public static void main(String[] args) {
//创建一个固定数量的线程池
// 1. 创建一个操作无界队列且固定大小线程池
ExecutorService pool1 = Executors.newFixedThreadPool(10);
//线程池中线程的数量是动态变化的
// 2. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
ExecutorService pool2 = Executors.newCachedThreadPool();
//线程池中只有一个线程
// 3. 创建一个操作无界队列且只有一个工作线程的线程池
ExecutorService pool3 = Executors.newSingleThreadExecutor();
//线程池中只有一个线程+定时器功能
// 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
ExecutorService pool4 = Executors.newSingleThreadScheduledExecutor(Executors.defaultThreadFactory());
//创建一个固定数量的线程池+定时器功能
// 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
ExecutorService pool5 = Executors.newScheduledThreadPool(3, Executors.defaultThreadFactory());
// 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
ExecutorService pool6 = Executors.newWorkStealingPool();
// 7. 自定义线程池
ExecutorService pool7 = new ThreadPoolExecutor(3,
10,
10000,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
}
- 核心操作为 submit, 将任务加入线程池中
- 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
- 使用一个 BlockingQueue 组织所有的任务
- 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
- 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增线程了.
import java.util.concurrent.LinkedBlockingQueue;
class Worker extends Thread {
private LinkedBlockingQueue queue = null;
public Worker(LinkedBlockingQueue queue) {
super("worker");
this.queue = queue;
}
@Override
public void run() {
// try 必须放在 while 外头, 或者 while 里头应该影响不大
try {
while (!Thread.interrupted()) {
Runnable runnable = queue.take();
runnable.run();
}
} catch (InterruptedException e) {
}
}
}
public class MyThreadPool {
private int maxWorkerCount = 10;
private LinkedBlockingQueue queue = new LinkedBlockingQueue();
public void submit(Runnable command) throws InterruptedException {
if (queue.size() < maxWorkerCount) {
// 当前 worker 数不足, 就继续创建 worker
Worker worker = new Worker(queue);
worker.start();
}
// 将任务添加到任务队列中
queue.put(command);
}
}
严重线程池是否模拟实现完成 代码运行如下:
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool();
for (int i = 0; i < 10; i++) {
int j=i;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello"+j);
}
});
Thread.sleep(1000);
}
运行结果如下:
写在最后:
以上就是本文全部内容,如果对你有所帮助,希望能留下你的点赞+关注,我会更加努力的更新内容!非常感谢
若本篇文章有错误的地方,欢迎大佬们指正!