什么是线程池:
在Java中,池的设计都是为了提高资源的重用性和系统的性能,降低资源创建和销毁的开销。线程池达成的也是类似的效果。我们创建和销毁线程会消耗系统资源,即使线程是轻量级的进程但是仍然会造成不小负担。
- 厨房中的厨师就是线程: 想象一下,你的厨房里有一群厨师,每个厨师都是一个线程,负责烹饪不同的菜肴。
- 顾客点菜就是任务: 每当顾客来点菜,相当于有一个新的任务进入系统,需要厨师去做。
- 任务的处理速度: 假设每个菜肴需要一定的时间来烹饪。如果每次有顾客点菜都要招募一个新的厨师(创建新线程),那么会增加很多开销,比如为新厨师分配工作空间、准备厨具等,这就是线程创建和销毁的开销。
- 线程池的作用: 相反,如果你事先准备好一群厨师(线程池),当顾客点菜时,只需从这群厨师中选一个空闲的厨师来做即可,不需要再额外招募新的厨师(创建新线程)。
- 控制厨师数量: 另外,你可能会考虑到厨房的大小和工作效率,不会让太多的厨师同时在厨房里工作(控制线程数量),以免拥挤和混乱。
如果我们需要做饭直接叫没活干的厨师来做就行了,不用再去外面找一个厨师。就大大提升了效率。
java库中的线程池:
ThreadPoolExecutor类
我们经常使用的是ThreadPoolExecutor这个线程池的实现,可以写入核心线程数、最大线程数、线程存活时间等一些相关的配置来调整线程池的状态。
面试题:线程池的的构造方法参数。
这里一共有七个参数:
- corePoolSize和maximunPoolSize:表示核心线程数和最大线程数,如果任务没有那么繁忙,通过核心线程就能解决,如果比较繁忙的话就得调用临时线程,核心线程+临时线程<=最大线程数。
- keepAliveTime和unit: 表示最长空闲时间和设置该空闲时间的单位。 线程在一定时间内不执行,系统就会自动回收。
- workQueue:是一个任务队列,线程提供submit方法 ,其他线程将任务提交给线程池,也就是将任务交到这个任务队列。
- threadFactory:是线程工厂,也就是创建线程的地方,
- handler:任务量超出负荷拒绝策略,
Executors类:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo20 {
public static void main(String[] args) {
ExecutorService service=Executors.newFixedThreadPool(4);
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
}
通过submit方法将重写run方法并且执行。
创建线程有两种方式:
1.通过Executors类:
- 如果需要一个简单的线程池来执行任务,而且不需要对线程池的参数进行配置,那么可以使用 Executors 提供的工厂方法
- Executors 提供了许多预定义的线程池类型,使用这些工厂方法可以更加方便地创建标准类型的线程池,而无需手动配置线程池的参数。
自定义线程池的情况:
- 实际上自定义线程就是根据自己的需要来进行定义,能够满足更多需求,但是不走也相对繁琐。
自己实现一个线程池
我们将会自己实现一个线程池
线程池包括的内容:
- 核心操作为 submit,将任务加入到线程池中
- 要有n个工作线程,使用 Runnable 描述一个任务
- 使用一个 BlockingQueue 组织所有的任务
- 每个工作线程要做的事情:不停的从 BlockingQueue 中取任务执行
- 指定一下线程池中的最大线程数 maxWorkerCount, 当前线程数达到这个最大值时,就不在新增线程了
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class MyThreadPool {
private BlockingQueue<Runnable> blockingQueue=new ArrayBlockingQueue<>(1000);
public void submit(Runnable runnable) throws InterruptedException {
blockingQueue.put(runnable);
}
public MyThreadPool(int n) {
for (int i = 0; i <n ; i++) {
Thread thread=new Thread(()->{
try {
Runnable runnable = blockingQueue.take();
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread.start();
}
}
}
public class Demo21 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool=new MyThreadPool(129);
for (int i = 0; i <1000 ; i++) {
int id=i;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("数字"+id);
}
});
}
}
}
public class Demo21 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool=new MyThreadPool(129);
for (int i = 0; i <1000 ; i++) {
int id=i;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("数字"+id);
}
});
}
}
}
这里有一个小知识点就是变量捕获的问题,所有我们得用 一个id来捕获i。
对于日常开发环境来说,我们常常使用官方提供的线程池就可以了。