目录
2:线程池原理底层介绍(ThreadPoolExecutor)
2.3:线程池原理分析(网上找的图,详见java并发编程艺术)
1:为什么时候用线程池?
使用线程池的优点:
第一:降低资源消耗,通过重复利用已经创建的线程减少线程的创建和销毁造成的消耗
第二:提高效应速度,当任务到达的时候,不用等待线程的创建就能执行
第三:提高线程的客观理性,线程是稀缺资源,不能无限创建,会对系统造成消耗,使用线程池方便对线程进行统一的管理。
2:线程池原理底层介绍(ThreadPoolExecutor)
在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。
2.1:正确创建线程池和参数分析
ExecutorService executorService;
executorService = new ThreadPoolExecutor(
5,//核心线程
10,//最大线程
3,//(最大线程-核心线程)的存活时间
TimeUnit.SECONDS,//单位
//new LinkedBlockingDeque<>(),// 链表的阻塞对列 长度为3 默认长度超级大
new ArrayBlockingQueue<>(10),//数组的有界队列
//new SynchronousQueue<>(),//同步队列 只能存一个 必须取出来 才能再次存
Executors.defaultThreadFactory(),//默认工厂
//new ThreadPoolExecutor.AbortPolicy()//默认策略 队列+最大值 满了 抛出异常
//new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里 main线程处理 不会异常
new ThreadPoolExecutor.DiscardPolicy()//队列满了丢掉任务
);
线程池参数解析:
1:int corePoolSize 初始化线程池5个
2:int maximumPoolSize 最大线程池10
3:long keepAliveTime 当前程存活时间 无事可做>1秒 并且线程池数量大于初始化线程5个的时候会被回收到corePoolSize大小
4:TimeUnit unit 时间单位
5:BlockingQueue<Runnable> workQueue 阻塞队列 当请求>核心线程(注意不是最大线程) 进入阻塞队列
线程池推荐三种阻塞队列:
LinkedBlockingQueue:基于链表结构的无界阻塞队列,它可以指定容量也可以不指定容量(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)
ArrayBlockingQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
6:Executors.defaultThreadFactory() 默认线程工厂
指定创建线程的工厂,可以不指定,默认是Executors.defaultThreadFactory(),也就是给线程设置一些name信息pool-1-thread-8(代表线程号)
7:defaultHandler 默认处理器,线程池队列放不下了,咋办
类名 | 作用 |
ThreadPoolExecutor.AbortPolicy() | 直接抛异常 |
ThreadPoolExecutor.CallerRunsPolicy() | 由向线程池提交任务的线程来执行该任务 |
ThreadPoolExecutor.DiscardPolicy() | 抛弃当前的任务 |
还是默认的AbortPolicy()合理,队列放不下,直接异常
2.2:错误创建线程池
既然有正确的创建线程池,当然也有不好的创建方法,Executors类的底层实现便是ThreadPoolExecutor! Executors 工厂方法有:
Executors.newCachedThreadPool():线程池大小=Integer.MAX_VALUE,无界线程池,可以进行自动线程回收
Executors.newFixedThreadPool(int):固定大小线程池
Executors.newSingleThreadExecutor():线程池大小=1
它们均为大多数使用场景预定义了设置。不过在阿里java文档中说明,尽量不要用该类创建线程池。
//创建newCachedThreadPool
ExecutorService executorService= Executors.newCachedThreadPool();
//源码分析 调用了ThreadPoolExecutor 最大线程数是Integer.MAX_VALUE 显然不合适,
//线程总数太JB大了,会导致内存溢出
//其他的几种线程池也有问题
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2.3:线程池原理分析(网上找的图,详见java并发编程艺术)
这个图就是ThreadPoolExecutor的线程池主要的处理流程,我们第三部的代码会验证这个图的具体流程
线程池的大体工作思路
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize数的线程,空闲时间达到keepAliveTime时,关闭空闲线程 6.当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭
2.4:底层源码验证
int c = ctl.get();
//工作线程小于核心线程数,则调用addWork创建新的线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//工作线程大于核心线程,并且线程池是run的时候将线程放入对列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//线程添加到addWork失败则抛出异常
else if (!addWorker(command, false))
reject(command);
3:线程池实战(ThreadPoolExecutor)
3.1:代码示例
我们建立一个线程池核心线程是5、最大线程是10、对列长度是12,然后创建20个任务来使用这些线程池。代码如下
public class MyThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
threadPoolExecutor = new ThreadPoolExecutor(
5,//初始化线程池5个
10,//最大线程池10
1,//当线程存活时间 无事可做>1秒 并且线程池数量大于初始化线程5个的时候会被回收
TimeUnit.SECONDS,//时间单位
//1:ArrayBlockingQuene基于数组的有界对列,先进先出
//2:LinkedBlockingQueue基于链表的阻塞无界对列,先进先出,未指定大小为Integer.MAX_VALUE
//3:SynchronizeQueue,一个不存储元素的阻塞对列,每一个插入操作要等另外一个线程的移除操作
//4:PriorityBlockingQueue一个具有优先级的无限阻塞对列
new ArrayBlockingQueue<>(12)//阻塞队列 当请求>核心线程(注意不是最大线程) 进入阻塞队列
);
for (int i = 0; i < 30; i++) {
//此处线程睡眠3秒
threadPoolExecutor.execute(new MyThread());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//shutdown 将线程池转为shutdown 不会提交新的线程,等待线程执行完毕
threadPoolExecutor.shutdown();
//shutdown 将线程转为stop状态 不允许提交新的任务,也不会处理阻塞队列中未执行的任务,
//threadPoolExecutor.shutdownNow();//正在执行的线程立马中断sleep interrupted
}
}
}
Myrunable代码如下:
public class MyThread implements Runnable{
@Override
public void run() {
try {
// 创建一个Date对象表示当前时间
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = sdf.format(date);
System.out.println("线程名字开始执行:"+Thread.currentThread().getName()+"当前时间:"+currentTime);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.2:输出结果分析(验证了前边所说的线程池处理流程)
4:线程池返回值代码
参考博文