这里写目录标题
- JUC介绍
- 基本概念
- JUC中的类和接口
- 主要包含功能
- 线程池
- 为什么使用线程池
- 什么是线程池
- 使用线程池的特点
- 优点
- 缺点
- JUC中的线程池
- Executor介绍
- ThreadPoolExecutor
- ThreadPoolExecutor是JUC中提供的默认线程池实现类
- 构造方法
- 参数详解
- corePoolSize 核心线程数
- 2. workQueue 阻塞队列
- 3. maximumPoolSize 最大线程数
- 4. keepAliveTime 线程最大空闲时间
- 5. unit keepAliveTime 时间单位
- 6. threadFactory 线程工厂
- 7. handler 线程池拒绝策略(面试题:线程池拒绝策略有哪些)
- 8. 线程池总结 (常见面试题)
- Executors
- newCachedThreadPool()
- newFixedThreadPool(int)
- newScheduledThreadPool(int)
- newWorkStealingPool()
- ForkJoinPool介绍
- 线程池代码演示
- JUC中的AtomicInteger原子类
- 原子性介绍
- JUC中原子类
- 使用AtomicInteger保证线程安全
- 原始方式: 使用synchronized同步锁解决
- 原子类方式: 使用AtomicInteger原子类
- 原子类AtomicInteger方法介绍
- 原子类AtomicInteger底层实现
- Volatitle(面试题)
- 可见性
- 可见性原理
- 嗅探机制工作原理
- 有序性
- CAS算法(面试题)
- CAS算法介绍
- 优点
- 缺点
- ABA问题
- 解决ABA问题
- 源码分析AtomicInteger原子类
- 底层实现
- 源码重点
JUC介绍
基本概念
从Java 5开始,在JDK中多出了java.util.concurrent包(简称:JUC)。
JUC主要是让开发者在多线程编程中更加简单、方便一些。
通过JDK内置了一些类、接口、关键字,补充完善了JDK对于并发编程支持的“短板
JUC中的类和接口
通过IDEA可以看java.util.concurrent包中所有的类和接口
主要包含功能
Executor:线程池
Atomic:原子操作类
Lock:锁
Tools:信号量工具类
并发集合:提供了线程安全的集合类。
线程池
为什么使用线程池
线程的创建和线程的销毁都会消耗一定的系统资源。
如果需要频繁的新建线程、销毁线程,对于系统可能需要消耗大量资源。
如何在需要频繁新建、销毁线程的场景下保证系统响应时间更快,消耗资源更少?
只有使用线程池
什么是线程池
内存中的一块空间。这块空间里面存放一些已经实例化好的线程对象。
当代码中需要使用线程时直接从线程池获取。当代码中线程执行结束或需要销毁时,把线程重新放入回到线程池,而不是让线程处于死亡状态
使用线程池的特点
优点
1.降低系统资源消耗。通过重用线程对象,降低因为新建和销毁线程产生的系统消耗。
2.提高系统响应速度。直接从内存中获取线程对象,比新建线程更快。
提供线程可管理性。通过对线程池中线程数量的限制,避免无限创建线程导致的内存溢出或CPU资源耗尽等问题
缺点
默认情况下,无论是否需要使用线程对象,线程池中都有一些线程对象,也就是说会占用一定内存
JUC中的线程池
Executor介绍
线程池的顶级接口,所有线程的子类
ThreadPoolExecutor
ThreadPoolExecutor是JUC中提供的默认线程池实现类
构造方法
提供了四种参数列表的构造方法
其中包含7个参数的构造方法, 这是ThreadPoolExecutor支持的所有参数
参数详解
corePoolSize 核心线程数
创建线程池后,默认线程池中并没有任何的线程,执行了从线程池获取线程的时候才会创建核心线程并返回. 如果没有达到指定的corePoolSize, 即使有空闲的核心线程, 也会创建新的核心线程执并返回, 直到达到了corePoolSize. 达到corePoolSize后, 从线程池获取线程, 有空闲的核心线程, 就返回空闲的线程
理解: 线程池就相当于一个公司, 核心线程数就相当于正式工人数, 新公司成立, 接一个新任务就会招聘一个正式工, 即使有空闲的正式工, 来了新任务也会招聘新的正式工来完成新任务, 直到招满为止. 招满后, 新任务就由空闲的正式工来完成
2. workQueue 阻塞队列
当线程池中的线程数目达到指定的corePoolSize后,并且所有的核心线程都在使用中, 再来获取线程将会被添加到缓存任务的阻塞队列中,也就是workQueue
队列可以设置queueCapacity 参数,表示任务队列最多能存储多少个线程对象
理解: 正式工已经招满, 此时所有的正式工都在忙着工作, 再来的新任务, 就只能排队等待
3. maximumPoolSize 最大线程数
被使用的线程数大于或等于核心线程数,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。
被使用的线程数等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常
理解: 正式工已经招满, 此时所有的正式工都在忙着工作, 且队列中的新任务已达到队列上线, 需要招聘临时工, 来完成任务. 正式工和临时工的总数为公司的最大员工数, 如果所有的临时工也都有任务, 再来新任务公司予以拒绝
4. keepAliveTime 线程最大空闲时间
线程池中没有被使用的线程, 就会处于空闲(alive)状态, 只要超过keepAliveTime, 空闲的线程就会被销毁,直到线程池中的线程数等于corePoolSize
如果设置了allowCoreThreadTimeOut=true(默认false),核心线程也可以被销毁
理解: 到了淡季, 公司不忙了, 很多的正式工和临时工都空闲了, 临时工就直接解雇了, 而正式工没有重大违纪的不会解雇
5. unit keepAliveTime 时间单位
TimeUnit是枚举类型
包含如下时间单位
注:
1秒 = 1000毫秒 1毫秒 = 1000微秒 1微妙 = 1000纳秒
6. threadFactory 线程工厂
主要用来创建线程
7. handler 线程池拒绝策略(面试题:线程池拒绝策略有哪些)
只有核心线程都在使用中,任务队列已满,且线程数量已经达到maximunPoolSize才会触发拒绝策略。或在调用shutdown()和真正关闭线程池之间提交的任务都会被决绝。因为线程池被shutdown()时,会等待线程池内线程对象执行结束,才关闭线程池
DiscardoldestPolicy: 从队列中去掉一个最先进入队列的任务。然后重新提交被拒绝的任务(重复此过程)
AbortPolicy:丢弃任务,抛出运行时异常(RejectedExecutionException)(默认值)
CallerRunsPolicy:由调用该任务的线程处理, 线程池不参与
DiscardPolicy:丢弃任务,不抛出异常
8. 线程池总结 (常见面试题)
corePoolSize: 核心线程数大小
maximunPoolSize:最大线程数大小(包含核心线程数,剩下的就是普通线程数)
queueCapacity:任务队列最大长度
keepAliveSize:线程最大空闲时间
allowCoreThreadTimeOut:核心线程超时是否被销毁(默认 false)
handler:拒绝策略
workQueue:任务队列类型
1.当线程数小于核心线程数时,创建线程, 直到到达核心指定的核心线程数
2.当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列
3.当线程数大于等于核心线程数,且任务队列已满
若线程数小于最大线程数,创建线程, 直到达到最大线程数
若线程数等于最大线程数,抛出异常,拒绝任务
Executors
Executors可以帮助快速实例化特定类型的线程池对象, Executors属于一个工具类
返回值都是ExecutorService接口的实现类, 底层都是调用ThreadPoolExecutor()
newCachedThreadPool()
不需要要参数,基本上都是默认值
构造方法参数:
corePoolSize: 0
maximumPoolSize: Integer的最大值
keepAliveTime: 60秒
workQueue:SynchronousQueue
同步队列, 一个线程向队列put(放入)数据后,阻塞等待被take(取出)
效果总结:线程需要时就新建,空闲60秒后被销毁。默认都是放在同步队列中。