线程池的分析
1、线程在java中是一个对象,更不是操作系统的资源,线程创建、销毁都需要时间。如果创建时间+销毁时间>执行任务的时间就非常不划算。
2、java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈大小1M,这个栈空间是需要从系统内存中分配的。线程过多会很消耗内存(操作系统频繁的切换线程也会影响性能)。
线程池的推出,就是为了方便控制线程的数量。
概念:
1、线程池管理器:用于创建并管理线程池,包括创建线程池、销毁线程池,添加新任务。
2、工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口:每个任务必须实现的接口,供工作线程调度任务的执行,主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列:用于存放没有处理的任务,提供一种缓冲机制。
核心线程数量、最大线程数量、队列大小的关系;
1、是否达到核心线程数量?没达到,创建新的线程来执行;
2、工作队列是否已满? 没满,将新提交的任务存储到工作队列中;
3、是否达到线程池最大数量?没达到,创建新的线程来执行,直到到达线程池最大数量
4、最后执行拒绝策略来处理这个任务(到达线程池最大数量后的线程,程序会拒绝执行)
1、ThreadPoolExecutor标准线程池实现的使用方式
1、线程池信息: 核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
结果:线程池线程数量为:5,超出数量的任务,其他的进入队列中等待被执行
2、 线程池信息: 核心线程数量5,最大数量10,队列大小3(也就是最大容纳13个任务),超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的.
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
new LinkedBlockingQueue(3), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println("有任务被拒绝执行了");
}
});
预计结果: 5个任务直接分配线程开始执行;3个任务进入等待队列,队列不够用;临时加开5个线程来执行任务(5秒没活干就销毁);队列和线程池都满了,剩下2个任务,没资源了,被拒绝执行;任务执行,5秒后,如果无任务可执行,销毁临时创建的5个线程。
3、 线程池信息: 核心线程数量5,最大数量5,无界队列,超出核心线程数量的线程存活时间:5秒
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
预计结果:线程池线程数量为:5,超出数量的任务,其他的进入队列中等待被执行
2、ScheduledThreadPoolExecutor实现类的使用
ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
// 周期性执行某一个任务,线程池提供了两种调度方式,这里单独演示一下。测试场景一样。
// 测试场景:提交的任务需要3秒才能执行完毕。看两种不同调度方式的区别
// 效果1: 提交后,2秒后开始第一次执行,之后每间隔1秒,固定执行一次(如果发现上次执行还未完毕,则等待完毕,完毕后立刻执行)。
// 也就是说这个代码中是,3秒钟执行一次(计算方式:每次执行三秒,间隔时间1秒,执行结束后马上开始下一次执行,无需等待)
threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务-1 被执行,现在时间:" + System.currentTimeMillis());
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
// 效果2:提交后,2秒后开始第一次执行,之后每间隔1秒,固定执行一次(如果发现上次执行还未完毕,则等待完毕,等上一次执行完毕后再开始计时,等待1秒)。
// 也就是说这个代码钟的效果看到的是:4秒执行一次。 (计算方式:每次执行3秒,间隔时间1秒,执行完以后再等待1秒,所以是 3+1)
threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务-2 被执行,现在时间:" + System.currentTimeMillis());
}
}, 2000, 1000, TimeUnit.MILLISECONDS);