使用线程池的好处
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
为什么用线程池不用Thread 的 start
- 使用线程池的好处
- 有助于避免this逃逸问题
- 线程的构造函数还没返回之前其他线程就持有了这个对象,从而调用了还没有构造完全的对象的方法
线程池底层原理与使用
上图可知,最后调用已创建好线程的对象是ExecutorService(ThreadPoolExecutor是他的实现类AbstractExecutorService的子类)
- 首先创建Runnable 或者 Callable 接口的任务对象。
- 把创建完成的实现 Runnable/Callable接口的 对象直接交给 ExecutorService(ThreadPoolExecutor对象)执行(这个过程就是把任务提交到线程池当中)
- .submit() 执行有返回值 返回一个futureTask对象,用来封装异步执行的结果
- .execut()执行返回没有返回值
- 主线程可以执行 FutureTask.get()方法来等待任务执行完成。也可以使用FutureTask.cancel(boolean 是否执行)来取消任务执行
ThreadPoolExecutor
线程池实现类 ThreadPoolExecutor 是 Executor 框架最核心的类。也是集团建议使用的线程池类
ThreadPoolExecutor最重要的参数(前三个最重要)
详情查看面经多线程43题前后的几题都要掌握
ThreadPoolExecutor 原理分析
线程池收到提交来的任务
- 判断核心线程池是否已满
- 没满创建线程执行任务
- 满了查看等待队列是否已满
- 等待队列没满,加入等待队列
- 满了查看线程池满了没有
- 没满创建线程
- 满了按照拒绝策略处理
- 主要方法 addWorker:用来创建新的工作线程
- 主要方法runWorker:while循环带哦用getTask方法从workerQueue里读取任务,然后执行任务。只要getTask方法不返回null,就一直不结束
- 主要方法getTask:
- 如果当前线程数大于corePoolSize,会调用workQueue的poll方法获取任务
- 如果从队列获取任务时间超过了最大生存时长,也会返回null
- 如果当前线程数小于corePoolSize,会调用workQueue的take方法阻塞在当前
- 如果当前线程数大于corePoolSize,会调用workQueue的poll方法获取任务
countDownLatch原理
countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
三个方法
- await():调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
- await(long timeout, TimeUnit unit) 和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
- countDown() :将count值减1