🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
线程池是一个在多线程场景中运用很广泛的并发框架,需要异步执行或并发执行任务的程序都可以使用线程池。有任务到来时,如果不使用线程池,我们需要不断的创建/销毁线程,还需要对线程进行管理;而使用线程池,直接将任务提交到线程池即可。使用线程池有几个好处:无需重复创建/销毁线程,降低资源消耗;提高程序响应速度;提高线程的可管理性。 ## 3.1 实现原理 线程池内部一般包含一个核心线程池,其内部的线程在创建之后一般不会销毁,执行完任务后线程会阻塞等待新任务到来。 当向线程池提交任务时,线程池会做如下判断: * 核心线程池未满,创建线程执行任务 * 核心线程池已满,若等待队列未满,则加入到等待队列;若等待队列已满但线程池未满,创建新线程执行任务;若等待队列和线程池均已满,则按照指定策略退出/拒绝任务/丢弃任务等。 ![线程池执行流程](http://web.uxiaowo.com/netty/Future/ExecutorFlow.jpg) 了解了实现原理,我们先来自己实现一个线程池,首先定义线程池的接口 **ThreadPool** 线程池的接口里面最重要的方法是execute执行任务 ``` public interface ThreadPool<Job extends Runnable> { //提交一个Job,这个Job需要实现Runnable接口 void execute(Job job); //关闭线程池 void shutdown(); //增加工作者线程 void addWorkers(int num); //减少工作者线程 void removeWorker(int num); //得到正在等待执行的任务数量 int getJobSize(); } ``` **CommonThreadPool** 在实现线程池时,我们需要定义线程池的大小,以及保存任务的列表jobs,下面是变量定义: ``` // 线程池最大限制数 private static final int MAX_WORKER_NUMBERS = 100; // 线程池默认的数量 private static final int DEFAULT_WORKER_NUMBERS = 1; // 线程池最小数量 private static final int MIN_WORKER_NUMBERS = 1; // 工作列表 private final LinkedList<Job> jobs = new LinkedList<Job>(); ``` 在线程池初始化时,我们要将核心线程池进行初始化,创建多个Worker线程,然后启动Worker线程。 ``` // num 为DEFAULT_WORKER_NUMBERS 默认线程池大小 private void initializeWokers(int num) { // 创建多个线程,加入workers中,并启动 for (int i = 0; i < num; i++) { Worker worker = new Worker(); workers.add(worker); Thread thread = new Thread(worker, "ThreadPool-Worker-" + threadNum.getAndIncrement()); thread.start(); } } ``` Worker启动后,一直没有任务,需要阻塞在jobs上(jobs是上面定义的任务列表),Worker等待任务到来后唤醒获取队列中的任务并执行。下面的代码中,如果jobs为空,则线程等待; ``` // worker的代码,首先要获取jobs的锁, synchronized (jobs) { while (jobs.isEmpty()) {// 如果jobs是空的,则执行jobs.wait,使用while而不是if,因为wait后可能已经为空了,需要继续等待 try { jobs.wait(); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt();// 中断 return;// 结束 } } job = jobs.removeFirst();// 第一个job if (job != null) { try { job.run();//注意,这里是run而不是start,传入的Job } catch (Exception e) { // 忽略Job执行中的Exception e.printStackTrace(); } } } ``` 提交任务时,只需要将任务加入jobs中,然后通知worker线程即可。worker线程获得锁后会取第一个任务执行。执行完毕,若jobs为空,worker线程继续进行休眠等待任务到来。 ``` @Override public void execute(Job job) { if (job == null) return; synchronized (jobs) { jobs.addLast(job); jobs.notify(); } } ``` 完整的代码可以查看https://github.com/ssj234/JavaStudy_IO/tree/master/IOResearch/src/net/ssj/pool