1、线程池的引入背景
多进程是解决并发编程的方案,但是创建、销毁进程的开销很大;因此引入了线程,线程比进程要轻量很多,即便如此,如果在某些场景,需要频繁的创建销毁线程,那么此时创建销毁线程的开销也无法忽视了。为了解决这样的问题,引入线程池。
2、线程池的工作机制
引入线程池之后,再去使用线程的时候,不是说用的时候再去创建,而是提前创建好,放到一个“池子里”,当我们需要使用线程的时侯,直接从池子里取一个线程过来,当我们不需要这个线程的时候,就把这个线程还回池子里,此时我们的操作就会比创建销毁线程效率更高。线程池最大的好处就是减少每次启动、销毁线程的损耗。
3、标准库中的线程池
使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
返回值类型为 ExecutorService
通过 ExecutorService.submit 可以注册一个任务到线程池中.
public static void main(String[] args) {
//使用以下标准库中的线程池,先创建出一个线程池的实例
ExecutorService service = Executors.newFixedThreadPool(10);
//给这个实例里面加入一些任务
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
Executors 创建线程池的几种方式
newFixedThreadPool: 创建固定线程数的线程池
newCachedThreadPool: 创建线程数目动态增长的线程池.
newSingleThreadExecutor: 创建只包含单个线程的线程池.
newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer. Executors 本质上是 ThreadPoolExecutor 类的封装.
4、实现线程池
核心操作为 submit, 将任务加入线程池中
使用 Worker 类描述一个工作线程.,使用 Runnable 描述一个任务.
使用一个 BlockingQueue 组织所有的任务 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增 线程了.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadDemo28 {
static class Worker extends Thread{
private BlockingQueue<Runnable> queue = null;
public Worker(BlockingQueue<Runnable> queue){
this.queue = queue;
}
@Override
public void run() {
//工作线程的具体逻辑
//需要从阻塞队列中取任务
while (true){
try {
Runnable command = queue.take();
//通过run来执行这个具体的任务
command.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ThreadPool{
//包含一个阻塞队列,用来组织任务
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
//这个list就用来存放当前的工作线程
private List<Thread> workers = new ArrayList<>();
private static final int MAX_WORHKER_COUNT = 10;
//通过这个方法,把任务加入到线程池中
//submit不仅可以把任务放到阻塞队列中,同时也可以负责创建线程
public void submit(Runnable command){
if (workers.size() < MAX_WORHKER_COUNT){
//如果当前工作线程的数量不足线程数目上限,就创建出新的线程
//工作线程就专门搞一个类来完成
//work内部要能够取到队列的内容,就需要把这个队列实例通过Work 的工作方法传过去
Worker worker = new Worker(queue);
worker.start();
workers.add(worker);
}
queue.offer(command);
}
}
public static void main(String[] args) {
ThreadPool pool = new ThreadPool();
for (int i = 0; i < 10; i++){
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
}
}