关于线程池的一些笔记
线程池的优点
1.减少了资源的消耗(不用频繁创建,销毁线程),让线程可复用
2.提高了响应速度
3.方便管理线程
线程池的创建方式
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newSingleThreadScheduledExecutor();
ExecutorService executorService3 = Executors.newFixedThreadPool(1);
ExecutorService executorService = new ThreadPoolExecutor(
5, //核心进程数
8, //线程池允许存在的最大线程数
10,//超时等待
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(4), //等待队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()); //拒绝策略
corePoolSize:
线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。
maximumPoolSize:
线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。
如果当前队列已满而且池子里正在执行的线程数已经达到最大线程数,那么便会根据拒绝策略处理新增的任务
目前拒绝策略有四种:
1.AbortPolicy 抛弃掉新增任务并抛出异常
2.CallerRunsPolicy 在任务被拒绝添加后,会调用当前线程池的所在的线程去执行被拒绝的任务。
3.DiscardOldestPolicy 会去和最早进入队列的任务竞争,竞争成功会执行该任务,不成功也不会抛出异常
4.DiscardPolicy 直接抛弃,不抛出异常
最大线程数该如何设置
1.大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务。 CPU密集型,有几核就设置成几个,这样可以保持CPU效率最高(可以避免CPU频繁切换任务导致执行任务效率变低)
2.IO密集型 IO密集型任务指任务需要执行大量的IO操作,涉及到网络、磁盘IO操作,对CPU消耗较少,其消耗的主要资源为IO。我们所接触到的 IO ,大致可以分成两种:磁盘 IO和网络 IO。
磁盘 IO ,大多都是一些针对磁盘的读写操作,最常见的就是文件的读写,假如你的数据库、 Redis 也是在本地的话,那么这个也属于磁盘 IO。
网络 IO ,这个应该是大家更加熟悉的,我们会遇到各种网络请求,比如 http 请求、远程数据库读写、远程 Redis 读写等等。
IO 操作的特点就是需要等待,我们请求一些数据,由对方将数据写入缓冲区,在这段时间中,需要读取数据的线程根本无事可做,因此可以把 CPU 时间片让出去,直到缓冲区写满。既然这样,IO 密集型任务其实就有很大的优化空间了(毕竟存在等待):CPU 使用率较低,程序中会存在大量的 I/O 操作占用时间,导致线程空余时间很多,所以通常就需要开CPU核心数两倍的线程。
拓展
线程池实现定时任务
cheduledExecutorService executorService4 = Executors.newSingleThreadScheduledExecutor();
executorService4.scheduleAtFixedRate(new Thread(()->{
System.out.println("1");
}),1,120, TimeUnit.SECONDS);
executorService4.scheduleWithFixedDelay(new Thread(()->{
System.out.println("1");
}),1,120, TimeUnit.SECONDS);
System.out.println(Runtime.getRuntime().availableProcessors());
注意scheduleAtFixedRate和scheduleWithFixedDelay的区别
scheduleAtFixedRate 会在上一个任务开始执行设置的周期时间后,检查上一个任务是否有执行完,如果已经完成会重新进行,如果没有执行完会等待
scheduleWithFixedDelay 不会检查上一个任务是否完成,只是按固定的周期时间执行任务。
需要注意的是如果执行过程中出现异常后续任务将不会再执行,如果将异常捕获可以避免这个问题