线程池:
1、池化技术:优化资源的使用
2、线程池的好处:线程复用,控制最大并发数,管理线程
(1)降低资源的消耗:通过反复利用已经创建好的线程可以减少线程频繁的创建和销毁的资源消耗
(2)提高响应速度:当任务到达时,不需要等待线程的创建而直接执行
(3)提高线程的统一管理:线程时稀缺资源,线程被无限创建,不仅会消耗系统资源,还会降低系统的性能,使用线程池可以进行统一的管理、调优和监控
3、三大方法,七大参数,四大策略
3.1、三大方法
public class ThreadPool {
public static void main(String[] args) {
//Executors 工具类、3大方法
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//Executors.newFixedThreadPool(3);//固定线程的线程池
//Executors.newCachedThreadPool();//可伸缩的
//使用线程池创建线程
try {
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//程序结束,关闭线程池
threadPool.shutdown();
}
}
}
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程

ExecutorService threadPool =Executors.newFixedThreadPool(5);//固定线程的线程池,五个线程并发

ExecutorService threadPool =Executors.newCachedThreadPool();//可伸缩的,多个线程并发

3.2、七大参数
- 核心线程数:一般情况下工作的线程数
- 最大线程数:最大可以开的线程数
- 阻塞队列:最大线程数满了,之后来的线程会进入阻塞队列
- 存活时间:核心线程空闲一定时间后关闭线程池
- 存活时间单位:
- 线程工厂:创建线程的工厂
- 拒绝策略:阻塞队列也满了,之后的线程处理方式
源码:开启线程池调用的都是ThreadPoolExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//最大为21亿
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
源码ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize, //最大核心线程池大小
long keepAliveTime, //超时就会释放
TimeUnit unit, //超时单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂,创建线程
RejectedExecutionHandler handler) { // 拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
阿里手册

3.3、四大拒绝策略
- 中断并抛出异常
- 丢弃到达的任务(默认)
- 丢弃阻塞队列中等待时间最长的任务
- 让提交任务的线程去执行任务

创建线程池代码
public class ThreadPool {
public static void main(String[] args) {
// 自定义线程池!工作 ThreadPoolExecutor
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3, //超过3秒钟就释放线程池
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), //阻塞队列
Executors.defaultThreadFactory(), //默认线程工厂
//new ThreadPoolExecutor.AbortPolicy()); // 不处理,抛出异常
//new ThreadPoolExecutor.CallerRunsPolicy());//哪来的去哪里
//new ThreadPoolExecutor.DiscardPolicy());//队列满了不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,也不会抛出异常!
try {
// 最大承载:Deque + max
// 超过 RejectedExecutionException
for (int i = 1; i <= 9; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 执行");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}

只有两个核心线程在执行,未触发最大并发(最大设置为5)

此时,使用线程池创建6个线程,触发最大并发,线程3进入执行

达到最大承载量:线程池最大线程数+阻塞队列
超出最大的承载量,就抛出异常


结果:由main线程执行


4、线程池处理流程

5、线程池中线程复用原理
线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。
在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的 run 方法串联起来。
总结
最大线程如何定义
1、CPU 密集型,几核,就是几,可以保持Cpu的效率最高!
// 获取CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors()); //本机4核
2、IO密集型 大于程序中十分耗IO的线程数,一般是两倍
680

被折叠的 条评论
为什么被折叠?



