1:线程池原理-基本概念:
1:线程池管理器:用户管理线程池。包括创建线程池、销毁线程池,添加新任务等。
2:工作线程:工作线程就是线程池中实际工作的线程。没有任务时:处于等待状态,有任务时:可以循环的执行任务。
3:任务接口:每个任务都需要实现的接口。规范了任务的输入、输出等。
4:任务队列:任务太多时,超过了线程池处理能力。将待处理的任务放到等待队列中。
2:线程池接口和实现类:
1:接口:Executor:最上层的接口:定义了执行任务的方法:executor()
2:接口:ExecutorService:继承了Executor接口,扩展了Callable,Future,关闭方法。
3:接口:ScheduledExecutorService:继承了ExecutorService,怎加了定时任务相关的方法。
4:实现类:ThreadPoolExecutor:标准的线程池实现。但是比较基础。
5:实现类:ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor,实现了ScheduledExecutorService。也就是说:在标准的线程池类基础上怎加了定时任务。


3:ThreadPoolExecutorc测试:
/**
* 1:核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间5秒。
*/ // 没有达到线程池最大线程数量10,而是进入了等待队列。
private static void threadPoolExecutorTest1() throws Exception{ 当前线程池线程数量为:5
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 当前线程池等待队列的长度:5
5, // 核心线程数量 线程结束:7
10, // 最大线程数量 ...
5, // 超出核心线程数量的线程的存活时间 线程结束:14
TimeUnit.SECONDS, // 单位 当前线程池线程数量为:5
new LinkedBlockingQueue<Runnable>()); // 线程等待队列 当前线程池等待队列的长度:0
testCommon(threadPoolExecutor); // 因为没有把队列占满,只有等待队列满了才会超出核心线程数量
}
/**
* 1:等待队列长度为3,2: 指定拒绝策略。
*/
private static void threadPoolExecutorTest2() throws Exception{
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5, 10, 5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3), 等待队列调小为3
new RejectedExecutionHandler() { // 如果(等待队列满了 && 最大线程数量也达到了):执行拒接策略
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任务被拒绝执行。");
}
}
);
testCommon(threadPoolExecutor);
}
/**
* 1:核心线程数量 = 最大线程数 , 队列无界
*/
private static void threadPoolExecutorTest3()throws Exception{
// 和线程池工具类的 Executors.newFixedThreadPool(int nThreads)实现一样。 // 和Executors.newFixedThreadPool(int nThreads)实现一样
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5, 5, 5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
testCommon(threadPoolExecutor);
}
/**
* SynchronousQueue,实际上不是一个真正的队列,因为他不会为队列中的元素维护存储空间。他维护的是一组线程。
* 这种线程池起初没有线程,最后执行完成后也没有线程。
* 60秒后无用的线程就会销毁
*/
private static void threadPoolExecutorTest4() throws Exception{ // 和Executors.newCachedThreadPool() 实现一样
// 和 Executors.newCachedThreadPool() 方法一样 // 当无法预估需要多少线程,但是最大线程数量自己设定。不然太大了。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,Integer.MAX_VALUE, 60L,TimeUnit.SECONDS, // 如果运行过程中有线程,就复用。没有就加开。
new SynchronousQueue<Runnable>()); // 不工作的线程,60秒后自动销毁。
} // 注意控制最大线程数量。
/**
* 定时执行 : 3秒后执行,一次性执行,到时间就执行。
* 核心线程数:5 , 最大线程数为:Integer.MAX_VALUE
* DelayedWorkQueue 为延时队列,存放进去一定时间后才可以取出来。
* @throws Exception
*/
private static void threadPoolExecutorTest5() throws Exception{ // 定时任务,延时执行一次。
// Executors.newScheduledThreadPool()
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5); // 如果你的任务需要很多线程,就调多一点。
scheduledThreadPoolExecutor.schedule(new Runnable() {
@Override
public void run() {
System.out.println("任务执行");
}
},
3000,
TimeUnit.MICROSECONDS); // 3秒之后执行
}
/**
* 定时任务,周期性执行。(固定频率,固定延迟)
* 固定线程数:5 核心线程数5 最大线程数Integer.MAX_VALUE 延时队列,超出核心线程数量的线程存活0秒。
* 例如:需要定时查询数据库的任务。
*/
private static void threadPoolExecutorTest6() throws Exception{
// 周期性执行任务有两种调度方式。
ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
// 第一种:固定频率:提交后:2秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完成,等待完毕后立即执行下一个任务)
threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("如果任务执行时间超出了周期时间,下一个任务直接开始");
}
},2000 , 1000 ,TimeUnit.MICROSECONDS);
// 第二种:固定延迟:提交后:两秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完毕,等上一次完毕后,再等待一秒。然后执行下一个任务)
threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("如果任务执行时间超出了周期时间,下一个任务重新计算执行延时后再执行");
}
},2000,1000,TimeUnit.MICROSECONDS);
}
/**
* 测试:提交15个执行时间为3秒的任务。并输出线程状态。
* @param threadPoolExecutor 显示池
*/
private static void testCommon(ThreadPoolExecutor threadPoolExecutor)throws Exception{
for (int i = 0; i < 15; i++) {
int n = i;
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("开始时间:"+n);
Thread.sleep(3000L);
System.out.println("线程结束:"+n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("任务提交成功"+i);
}
// 等待5秒后看线程池状态
Thread.sleep(5000L);
System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size());
// 等待15秒,查看线程池状态(理论上,超出核心线程数量的线程会被销毁)
Thread.sleep(10000L);
System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size());
}

4:可以用线程池工具类快速创建线程池:

5:如何合适的定义线程池的线程数量:
1:计算型任务(计算数据任务):线程的数量是CPU熟练的 1~2倍即可。
2:IO型任务(网络操作,数据库操作,文件操作):根据具体的IO阻塞时长考量。就是说阻塞的时候开多线程执行。
3:考虑设置最小的线程数量和最大线程数量。自动在这个区间怎加线程数量。
4:窍门:通过监控CPU的使用率。达到80%就说明cpu利用起来了。
来源:https://www.cnblogs.com/Xmingzi/p/12601063.html