目录
1.线程池基础知识
1.1线程池概念
在多线程当中,并发程度会不断提升,随之带来的是线程频繁的创建和销毁,此时程序的开销就会变大,为了改善这个现象,就引入了线程池,程序将创建好的线程放入到“线程池”中,当程序需要用时,可以直接在这个“池子”中取出来,线程用完之后再放入到“池子”中。这样在就不会再频繁的创建和销毁了,可以使程序开销减小,效率更高。
1.2线程池使程序更高效的原理
在创建、销毁线程时我们时是交给操作系统内核来完成的, 而我们使用线程池调度是以用户态实现的。
如果将任务交给操作系统的内核去完成,它的完成时间是不可控的,这是为什么呢?原因在于:内核需要对多个任务提供服务,在你交给它任务时,它可能无法仅仅单一的完成你交与的任务,可能还需要完成别人交与它的任务。
而用户态就不一样了,用户态就仅会对自己的任务负责,因此效率更高,开销更小。
🐣下面这个例子可以让你更加容易理解(银行办事):
有一天你去银行办理业务,排队排了好久,工作人员问你:拿身份证复印件了吗?你心想:卧槽还要这个。你回答:没有拿。然后工作人员说,没关系,你有两个选择:
1.我给你打印然后办理。(相当于内核态)
2.你自己打印然后给我。 (相当于用户态)
选择1:当让工作人员打印,可能他手头上出了你的打印东西在这个事还有其他的事,因此让他帮打印的话,打印好就不知道事猴年马月了。
选择2: 我自己直接去大厅的复印机这里,直接就打印好了,然后拿给工作人员,办完就跑。
很明显,在1、2选择中,选择2更高效,这也就是为什么线程池更高效了。
2.标准库中的线程池
2.1线程池的继承关系
2.2线程池的构造方法
Java中提供了线程池的标准类(ThreadPoolExecutor),构造方法(常用的一个)如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> 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
被拒绝的任务的处理程序,抛出一个
RejectedExecutionException
。static class
ThreadPoolExecutor.CallerRunsPolicy
一个被拒绝的任务的处理程序,直接在
execute
方法的调用线程中运行被拒绝的任务,除非执行程序已经被关闭,否则这个任务被丢弃。static class
ThreadPoolExecutor.DiscardOldestPolicy
被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试
execute
,除非执行程序关闭,在这种情况下,任务被丢弃。static class
ThreadPoolExecutor.DiscardPolicy
被拒绝的任务的处理程序静默地丢弃被拒绝的任务。
-
2.3线程池的使用
线程池在实现时,我们需要用到工具类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<Runnable>());
}
}
2.线程池的优点
- 降低资源消耗:减少线程的创建和销毁带来的性能开销。
- 提高响应速度:当任务来时可以直接使用,不用等待线程创建
- 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。
3.线程池的模拟实现
- 核心操作为 submit, 将任务加入线程池中
- 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
- 使用一个 BlockingQueue 组织所有的任务
- 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
- 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增线程了.
import java.util.concurrent.LinkedBlockingQueue;
class Worker extends Thread {
private LinkedBlockingQueue<Runnable> queue = null;
public Worker(LinkedBlockingQueue<Runnable> 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<Runnable> 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);
}
运行结果如下:
写在最后:
🐣🐣🐣以上就是本文全部内容,如果对你有所帮助,希望能留下你的点赞+关注,我会更加努力的更新内容!非常感谢🧡🧡🧡
若本篇文章有错误的地方,欢迎大佬们指正!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/150280.html