线程池,又是一个池,我们已经见识过很多池了:
数据库连接池、字符串常量池....
那我们这个线程池又是个啥呢?
我们提前将线程准备好,需要用的时候直接取,不需要用的时候,在直接还回去。
这样就不需要去从系统中申请了。
这样做,最大的好处就是减少每次启动、销毁线程的损耗
池的目的就是为了提高效率。
为什么需要提高效率呢?
虽然线程对比于进程较为轻量,但是频繁的创建、销毁依旧开销很大。
为什么这也可以提升效率呢?
从线程池拿线程,时纯粹的用户态操作;
而从系统创建线程涉及到了用户态和内核态之间的切换;
真正的创建线程是要在内核态完成的。
举个栗子()
我们现在突然需要一个线程,我们直接可以从用户台中(前提是用户态中有线程);但是如果我们要从内核态中拿到这个线程,确实可以拿到,但是在拿的这个过程中内核还做了其他的很多事...
一旦涉及到了内核态时间就是不可控的。
用户态和内核态是操作系统中的基本概念:
内核态(Kernel Mode):运行操作系统程序,操作硬件
用户态(User Mode):运行用户程序
我们简单的理解:
一个操作系统 = 内核 + 配套的应用程序
我们先来写一个jdk 提供的线程池:
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
这里并不是使用 ThreadPool 而是使用 ExcutorService ;
工厂模式的概念:
主要就是用来填构造方法的坑的。
这里的工厂模式主要来自于ThreadPoolExecutor 这个原装线程池对象,上述工厂类是对这个原生类进行了封装,只要了解清楚这个类,其他工厂类就简单了。
我们再来重点解释一下 这几个参数的含义:
标准库中的四种拒绝策略:
拒绝策略类型 | 说明 | |
1 | ThreadPoolExecutor.AbortPolicy | 默认拒绝策略,拒绝任务并抛出任务 |
2 | ThreadPoolExecutor.DiscardPolicy | 使用调用线程直接运行任务 |
3 | ThreadPoolExecutor.DiscardOldestPolicy | 直接拒绝任务,不抛出错误 |
4 | ThreadPoolExecutor.CallerRunsPolicy | 触发拒绝策略,只要还有任务新增,一直会丢弃阻塞队列的最老的任务,并将新的任务加入 |
接下来我们手动实现一个线程池:
看代码:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class MyThreadPool {
private BlockingQueue queue = new LinkedBlockingQueue<>();
// 作用类似于生产者,给线程添加任务
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
// 此处实现一个固定线程数的线程池
public MyThreadPool(int n) {
// 循环内有一个工作线程
// 类似于生产者,每次去取和执行这个任务
for (int i = 0; i < n; i++) {
//
Thread t = new Thread(() -> {
try {
while (true) {
Runnable runnable = queue.take();
runnable.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(10);
for (int i = 0; i < 1000; i++) {
int number = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello " + number);
}
});
}
}
}
我们来跑一跑:
因为线程的调度是无序的,虽然是 按顺序添加的,但是结果仍然是个无序的状态。
1. 降低资源的消耗,利用已创建的线程,降低了线程的不断地创建和销毁的资源浪费 2. 提高相应小笼包,每次都是直接使用已经创建好的线程,不必去等待线程的创建 3. 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。
多线程的基础也就到这结束了,这六章属于是面试常考,工作中常用的,我们接下来还有进阶部分,这属于锦上添花的,能记下来最好。