一,线程那些事
线程对每个编程人员都了解,各种语言都支持线程,C语言pthread_create,java中的thread ,python中threading.Thread等等.
Java中的线程的状态:
- start:新创建的线程
- Ready:准备就绪的线程,由于CPU分配的时间片的关系,此时的任务不在执行过程中。
- Running:正在执行的任务
- Block:被阻塞的任务
- Time Waiting:计时等待的任务
- Terminated:终止的任务
开发过程中经常通过new thread创建子线程,这样是可以实现功能,只不过对线程的管理不方便,每次new Thread时,新建对象性能差。
线程缺乏统一管理,可能无限制新建线程,相互之间竞争,可能占用过多系统资源导致死机或oom。
缺乏更多功能,如定时执行、定期执行、线程中断
new thread代码:
public void test(){ Thread thread = new Thread(new Runnable() { @Override public void run() { while(true){//do something } } }); thread.start(); }
线程池的重要性,基本上涉及到跨线程的框架都使用到了线程池,比如说OkHttp、RxJava、LiveData以及协程等,
与新建一个线程相比,线程池的特点?
- 节省开销:线程池中的线程可以重复利用。
- 速度快:任务来了就能开始,省去创建线程的时间。
- 线程可控:线程数量可空和任务可控。
- 功能强大:可以定时和重复执行任务。
//corePoolSize:核心线程数量,不会释放。
//maximumPoolSize:允许使用的最大线程池数量,非核心线程数量,闲置时会释放。
//keepAliveTime:闲置线程允许的最大闲置时间。
//unit:闲置时间的单位。
//workQueue:阻塞队列,不同的阻塞队列有不同的特性。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);}
线程池的流程图:
当向线程池提交一个新的任务时,线程池有三种处理情况,分别是:创建一个工作线程来执行该任务、将任务加入阻塞队列、拒绝该任务。
提交任务的过程也可以拆分成以下几个部分:
- 当工作线程数小于核心线程数时,直接创建新的核心工作线程
- 当工作线程数不小于核心线程数时,就需要尝试将任务添加到阻塞队列中去
- 如果能够加入成功,说明队列还没有满,那么需要做以下的二次验证来保证添加进去的任务能够成功被执行
- 验证当前线程池的运行状态,如果是非RUNNING状态,则需要将任务从阻塞队列中移除,然后拒绝该任务
- 验证当前线程池中的工作线程的个数,如果为0,则需要主动添加一个空工作线程来执行刚刚添加到阻塞队列中的任务
- 如果加入失败,则说明队列已经满了,那么这时就需要创建新的“临时”工作线程来执行任务
- 如果创建成功,则直接执行该任务
- 如果创建失败,则说明工作线程数已经等于最大线程数了,则只能拒绝该任务了
在 runWorker 方法被调用之后,就是执行具体的任务了,首先需要拿到一个可以执行的任务,而 Worker 对象中默认绑定了一个任务,如果该任务不为空的话,那么就是直接执行。
执行完了之后,就会去阻塞队列中获取任务来执行,而获取任务的过程,需要考虑当前工作线程的个数。
- 如果工作线程数大于核心线程数,那么就需要通过 poll 来获取,因为这时需要对闲置的线程进行回收;
- 如果工作线程数小于等于核心线程数,那么就可以通过 take 来获取了,因此这时所有的线程都是核心线程,不需要进行回收,前提是没有设置 allowCoreThreadTimeOut