java中线程池1
不读书的人,思想就会停止 !
0. 思维导图
线程池是很多编程语言中都有的,虽然我现在很少写 java 了,但是对于面试或其他语言,其实都是相通的。
这篇是聊下线程的基本内容,下面是基本的思维导图。

1. 为什么需要线程池
任何程序,都是一些线程,执行一些任务组成的。任务会很多,如果我们每个任务都创建一个线程去执行。那么随着任务的增加,创建的线程数激增,会有很多问题:
系统可以创建的线程数不是无限的,因为线程需要占用内存等资源信息。 不是线程越多越好,线程越多,cpu 的核数是固定的,那么多个线程切换的时候,就会发生上下文切换影响性能。 线程创建和销毁本身也有开销,可能线程创建和销毁的时间比执行任务的时间还长,得不偿失。
public class OneTask {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.start();
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println("Thread Name: " + Thread.currentThread().getName());
}
}
}
如果只有一个线程执行任务那,又存在着 cpu 利用不充分的问题,比如我们很多任务可能需要 IO,在等待 IO 的时候,线程 sleep 会让出 cpu,从而不利于程序整体性能提升。
一个任务一个线程,系统执行慢;每个任务一个线程,又导致线程过多,导致一系列上述问题。
2. 线程池
为了让系统执行的足够快,又不想创建过多线程,占用过多资源,线程池就应运而生。 线程池有以下好处:
线程池,故名思意,池子里面创建了一定的线程数,任务来了之后,直接运行,提升响应速度。 线程池有不少线程是长期存在的,那就不需要过多的线程创建和销毁的开销。 线程池统筹 cpu 和内存的使用,线程不够的时候创建,线程空闲下来的时候可以进行销毁,在系统性能和资源占用中保持一个平衡。
3. Java 线程池参数说明
这些也是面试中常问的问题,Java 中线程池的参数说明如下:
corePoolSize 核心线程数 maxPoolSize 最大线程数 keepAliveTime+ 时间单位 空闲线程的存活时间 ThreadFactory 线程工厂,用来创建线程。 workQueue 用来存放任务的队列 Handler 处理拒绝任务接口实现 举例: 
3.1 线程池中线程数
Java 中的线程数开始的时候是 0 ,当添加任务的时候,首先查看线程池中线程数是否达到了核心线程数(corePoolSize),没有则创建; 如果达到了核心线程数,则看下队列是否满了,如果队列也满了,则判断限制线程数是否达到了最大线程数(maxPoolSize),如果没达到,则继续创建线程;如果达到了,则按照 Handler 处理拒绝任务的方法来处理。

从这个图需要注意的:
线程池初始的状态下是没有线程的,直到来了一个任务。 非核心线程数的创建是在队列满了之后再创建,如果使用无界队列,比如 LinkedBlockingQueue则队列永远不会满,也就不会创建非核心线程,线程池的最大线程数只能为 corePoolSize 。
3.2 keepAliveTime
keepAliveTime 和时间单位决定了非核心线程的存活时间,如果线程池的线程超过核心线程数,会根据这个时间来销毁非核心线程数,从而减少空闲时候的资源占用;任务多的话,又会根据上图规则来继续创建非核心线程。具备线程数弹性变化的能力。
3.3 ThreadFactory
创建线程的线程工厂,我们如果需要定制线程,比如设置线程的名称等,可以自定义线程工厂来替换默认的线程工厂来创建线程。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
3.4 workQueue
Java 线程池中的任务队列,常用的几种:
LinkedBlockingQueue
没有容量限制的队列,也真是因为无限,则有可能造成 OOM 问题。
SynchronousQueue
这个队列很奇怪,没有空间存放数据,来了任务直接交给线程,不进行存放,性能好。也许会奇怪,为啥要又这样队列,我觉得解耦是一方面,解耦生产者和消费者,比如 solr 的客户端我记得再 6.3 版本的时候就是用这种队列来提交任务的。
DelayedWorkQueue
延迟任务队列,当我们做任务定时调度的时候需要,使用此队列。采用堆这种数据结构,可以保持先执行的任务放在队头。 这个队列也是无界队列,也容易引起 OOM 问题。
四 线程拒绝任务
线程在两种情况下会拒绝任务:
线程池已经调用 shutdown 了,相当于已经关闭了线程池,当然再来的任务会被拒绝掉。 按照刚才线程池中线程的创建过程描述,当线程已经无法再创建非核心线程的时候,再来的任务就会被拒绝掉。
拒绝又分几种拒绝策略:
AbortPolicy: 拒绝抛异常RejectedExecutionException这是线程池默认的拒绝策略,如果需要拒绝,则抛出运行时异常:RejectedExecutionException调用者自行决定如何处理。DiscardPolicy: 这个比较狠,直接默默地抛弃,不通知,容易丢失任务。DiscardOldestPolicy: 这个是删除在队列中存活最久的任务,腾出空间给新来的任务,也是默默地抛弃,无提示。CallerRunsPolicy: 这个是我觉得比较好的策略,它不拒绝,如果满足拒绝条件后,哪个线程执行提交任务,就在哪个线程执行这个任务。你不是提交快慢,那我就交给你执行,这样可以防止提交太快导致无法执行的问题,线程执行了任务就会减慢了提交任务的速度,给线程池执行腾出了时间。
作者:明翼(XGogo)
-------------
公众号:TSparks
微信:shinelife
扫描关注我的微信公众号感谢
-------------

浙公网安备 33010602011771号