一、JUC
1.概念
从Java 5开始,在JDK中多出了java.util.concurrent包(简称:JUC)。
JUC主要是让开发者在 多线程编程 中更加简单、方便一些。
通过JDK内置了一些类、接口、关键字,补充完善了JDK对于并发编程支持的“短板”。
2.主要包含功能
-
-
Atomic:原子操作类
-
Lock:锁
-
Tools:信号量工具类
-
二、线程池
1.为何使用线程池
对于频繁创建和销毁的线程会消耗系统大量的资源,此时通过使用线程池可以使系统响应时间更快,消耗资源更少
2.概念
线程池是内存中的一块空间,其中存放着实例化的线程对象
当需要用到线程时从线程池中取出,执行完任务或需要销毁时再放回线程池,而不是让线程处于死亡状态。
3.使用线程池的特点
3.1 优点
1.降低系统资源的消耗。通过重用线程对象,减低由于新建和销毁线程造成的系统资源消耗
2.提高系统响应的速度。直接从内存中获取线程,比新建线程更快
3.提供线程的可管理性。通过对线程池中线程数量的限制,避免无限创建线程导致的内存溢出或CPU资源耗尽等问题。
3.2 缺点
默认情况下,无论是否需要使用线程,线程池中都有一些线程对象,占用内存。
三、JUC中的线程池
1.Executor
线程池的顶级接口
该接口中只有一个execute方法,参数为Runnable类型
public interface Executor { void execute(Runnable command); }
2.ThreadPoolExecutor
2.1 介绍
是JUC提供的默认的线程池的实现类
2.2 构造方法
四种有参构造
最多7个参数的构造方法
2.3 参数
2.3.1 corePoolSize-核心线程数
创建线程池后,线程池中默认是没有线程的,当从线程池获取线程执行任务时才创建核心线程来执行任务。
若没有线程数没有达到核心线程数corePoolSize,即使有空闲的核心线程,还是会创建新的核心线程来执行任务,直到达到核心线程数corePoolSize。
线程池中的线程数达到核心线程数corePoolSize后,从线程池获取空闲的核心线程执行任务。
2.3.2 workQueue-阻塞队列
队列:底层是数组或链表实现的,特点是先进先出
阻塞:队列为空时阻塞获取任务,队列满时阻塞添加任务。
作用: 当线程池中线程数量达到核心线程数corePoolSize时,且没有空闲的核心线程,再来获取线程执行任务,任务会被添加到缓存任务的阻塞队列workQueue中;
队列可以设置queueCapacity参数,表示最多能存储的任务数量
2.3.3 maximumPoolSize-最大线程数
当所有核心线程都在使用中、阻塞队列的任务也满了,线程池会创建新的线程执行任务,直到线程池中的线程数达到最大线程数maximumPoolSIze;
线程池中线程数已达到最大线程数且都在使用中、阻塞队列也满了,线程池对于新来的任务就会执行拒绝策略
2.3.4 keepAliveTime-线程最大空闲时间
线程池中的空闲线程空闲的时间超过了最大空闲时间keepAliveTime就会被销毁,直到线程池中的线程数等于核心线程数;
若设置allowCoreThreadTimeOut=true(默认false),核心线程也可以被销毁。
2.3.5 unit-时间单位
TimeUnit是枚举类型
public enum TimeUnit { //纳秒 NANOSECONDS(TimeUnit.NANO_SCALE), //微妙 MICROSECONDS(TimeUnit.MICRO_SCALE), //毫秒 MILLISECONDS(TimeUnit.MILLI_SCALE), //秒 SECONDS(TimeUnit.SECOND_SCALE), //分钟 MINUTES(TimeUnit.MINUTE_SCALE), //小时 HOURS(TimeUnit.HOUR_SCALE), //天 DAYS(TimeUnit.DAY_SCALE); private static final long NANO_SCALE = 1L; private static final long MICRO_SCALE = 1000L * NANO_SCALE; private static final long MILLI_SCALE = 1000L * MICRO_SCALE; private static final long SECOND_SCALE = 1000L * MILLI_SCALE; private static final long MINUTE_SCALE = 60L * SECOND_SCALE; private static final long HOUR_SCALE = 60L * MINUTE_SCALE; private static final long DAY_SCALE = 24L * HOUR_SCALE; }
2.3.6 threadFactory-线程工厂
创建线程对象
2.3.7 handler-线程池拒绝策略
前提:只有当任务队列已满,且线程数量已经达到maximunPoolSize才会触发拒绝策略。- AbortPolicy(默认):新提交的任务将被拒绝,并抛出RejectedExecutionException异常。
- CallerRunsPolicy:新提交的任务将由提交该任务的线程(调用execute()方法的线程)执行。这意味着提交任务的线程将会暂时充当一个临时线程来执行任务。
- DiscardPolicy:新提交的任务将被丢弃,不会抛出任何异常。
- DiscardOldestPolicy:新提交的任务将会替换掉等待队列中最旧的任务,然后尝试再次提交该任务。
2.4 创建线程总结
1. 当线程数小于核心线程数时,创建线程, 直到达到指定的核心线程数。
2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3. 当线程数大于等于核心线程数,且任务队列已满 。
i. 若线程数小于最大线程数,创建线程, 直到达到最大线程数 。
ii. 若线程数等于最大线程数,抛出异常,拒绝任务。
3. Excutors
Excutors属于一个工具类,可以快速实例化 特定类型 的线程池对象。
返回值都是ExecutorService接口的实现类,底层都调用了ThreadPoolExecutor()
3.1 SingleThreadExecutor()
方法代码如下:
参数:
核心线程数和最大线程数都为1
最大空闲时间为0
底层为链表的阻塞队列;
效果: 只会创建一个线程执行任务。
3.2 newFixedThreadPool( int )
方法代码:
核心线程数和最大线程数都是指定的nThreads
底层为链表的阻塞队列
实际线程数量永远维持在nThreads。
3.3 newCachedThreadPoole()
参数:
核心线程数:0
最大线程数:Integer的最大值(2^31-1)
最大空闲时间 60 秒
底层为同步队列的阻塞队列(没有存储空间,只要有任务就必须要有线程执行,如果没有找到空闲的线程就创建新的线程执行)
效果:
无限容量、线程空闲时间超过60秒就会被销毁、阻塞队列底层为同步队列,没有存储空间,只要有任务就必须有线程来处理,没有空闲的线程就创建新的线程来执行。
3.4 newScheduleThreadPool()
参数:
核心线程数为指定的corePoolSize
最大线程数为Integer最大值
最大空闲时间0
阻塞队列:DelayQueue队列
底层使用数组实现, 初始容量为16, 超过16个任务, 扩容到之前的1.5倍
可以实现 延时执行 和 周期执行
3.4.1 延时执行 schedule()