多线程
进程
-
进程同步机制
- 共享内存
- 信号量
- 管道
- 消息队列
- 套接字
-
进程的状态和转换
- 运行、就绪、等待
-
守护进程
- 守护进程就是在后台运行,不与任何终端关联的进程,
通常情况下守护进程在系统启动时就在运行,
它们以root用户或者其他特殊用户(apache和
postfix)运行,并能处理一些系统级的任务.
- 守护进程就是在后台运行,不与任何终端关联的进程,
-
孤儿进程
- 如果父进程先退出,子进程还没退出那么子进程将被 托孤给init进程
-
僵尸进程
-
一个进程已经终止了,但是其父进程还没有获取其状态
-
如何避免
- 在父进程创建子进程之前,就向系统申明自己并不会对这个子进程的
exit动作进行任何关注行为,这样的话,子进程一旦退出后,系统就
不会去等待父进程的操作,而是直接将该子进程的资源回收掉,也就
不会出现僵尸进程了 - fork两次,父进程fork一个子进程,然后继续工作,
子进程fork一个孙进程后退出,那么孙进程被init接管,
孙进程结束后,init会回收。不过子进程的回收还要自己做。 - 父进程通过wait和waitpid等函数等待子进程结束,
这会导致父进程挂起。
- 在父进程创建子进程之前,就向系统申明自己并不会对这个子进程的
-
线程
-
创建线程的方式
-
runnable
-
callable
- future
- future task
- Callable接口的任务线程能返回执行结果
-
Thread
-
一个类可以同时继承Thread和实现Runnable接口
-
实现Callable接口的任务线程能返回执行结果;
而实现Runnable接口的任务线程不能返回结果 -
future的底层实现异步原理
-
提供了三种功能
- 判断任务是否完成
- 能够中断任务
- 能够获取任务执行结果
-
future的底层实现异步原理
- 在客户端请求的时候,直接返回客户端需要的数据
(此数据不一定完整,只是简单的一点不耗时的操作),
但是客户端并不一定马上使用所有的信息,此时就有了
时间去完善客户需要的信息
- 在客户端请求的时候,直接返回客户端需要的数据
-
与FutureTask的区别和联系
- future是个接口,futuretask可以通过实现该接口,
调用get方法返回执行结果
- future是个接口,futuretask可以通过实现该接口,
-
-
-
守护线程
-
守护线程通常执行一些任务,当所有非守护线程终止的时候,J
VM简单的丢弃掉所有现存的守护线程.一旦其它非守护线程执
行完,不一定所有的守护线程都会执行完才退出,它们可能在非
守护线程执行完后的某个时刻退出 -
使用场景
- 来为其它线程提供服务支持.
-
-
线程上下文切换
-
线程的状态
- 创建、就绪、运行、阻塞、死亡
-
线程调度器
- 负责为runnable状态线程分配cpu时间
-
线程同步机制
-
临界区
- 指的是一个访问共用资源(例如:共用设备或是共用存储器)
的程序片段,而这些共用资源又无法同时被多个线程访问的特性
- 指的是一个访问共用资源(例如:共用设备或是共用存储器)
-
互斥量
- 锁
-
信号量
- volatile
-
事件
- 选择器epoll()
-
线程池
-
管理一组工作线程
-
等待执行任务的消息队列
-
创建线程池的方式
-
创建线程池
-
newSingleThreadExecutor(单线程线程池)
-
适用场景
- 适用于串行执行任务的场景
-
-
newFixedThreadPool(固定大小线程池)
-
特点
- coresize和maxsize相同
- 队列用的LinkedBlockingQueue无界
- keepAliveTime为0
-
工作机制
- 1.线程数少于核心线程数,新建线程执行任务
- 2.线程数等于核心线程数后,将任务加入阻塞队列
- 3.执行完任务的线程反复去队列中取任务执行
-
适用场景
- 适用于处理CPU密集型的任务,
确保CPU在长期被工作线程使用的情况下,
尽可能的少的分配线程即可。一般Ncpu+1
- 适用于处理CPU密集型的任务,
-
-
newCachedThreadPool(可缓存线程的线程池)
-
特点
- 核心线程数为0,且最大线程数为Integer.MAX_VALUE
- 阻塞队列是SynchronousQueue
- keepAliveTime为60s
-
工作机制
- 1.没有核心线程,直接向SynchronousQueue中提交任务
- 2如果有空闲线程,就去取出任务执行;
如果没有空闲线程,就新建一个 - 3.执行完任务的线程有60秒生存时间,
如果在这个时间内可以接到新任务,
就继续,否则结束生命
-
适用场景
- 用于并发执行大量短期的小任务
-
-
newScheduledThreadPool
-
特点
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是DelayedWorkQueue
-
工作机制
- 1.线程从 DelayQueue 中获取 time 大于等于当前时间的
ScheduledFutureTask(DelayQueue.take()) - 2.执行完后修改这个 task 的 time 为下次被执行的时间
- 3.再把这个 task 放回队列中
- 1.线程从 DelayQueue 中获取 time 大于等于当前时间的
-
适用场景
- 用于需要多个后台线程执行周期任务,
同时需要限制线程数量的场景。
- 用于需要多个后台线程执行周期任务,
-
-
-
两种启动线程池的方法:submit(有返回值)和execute(无返回值)
-
线程池的核心
-
生产者消费者模型
- 生产者将需要处理的任务放入队列
- 消费者从任务队列中取出任务处理
-
-
线程池的参数(ThreadPoolExecutor)
-
maximumPoolSize:最大线程数
-
corePollSize:核心线程数
-
keepAliveTime:空闲的线程保留的时间
-
workQueue任务队列):
用于保存等待执行的任务的阻塞队列 -
ThreadFactory
- 用于设置创建线程的工厂,可以通过线程工厂给每个
创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等
- 用于设置创建线程的工厂,可以通过线程工厂给每个
-
RejectedExecutionHandler(饱和策略)
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉。
-
TimeUnit:空闲线程的保留时间单位
-
-
-
单机上一个线程正在处理服务,如果忽然断电了怎么办
(正在处理和阻塞队列里的请求怎么处理)- 阻塞队列持久化,正在处理事物控制。断电之后正在处理的回滚,
日志恢复该次操作。服务器重启后阻塞队列中的数据再加载
- 阻塞队列持久化,正在处理事物控制。断电之后正在处理的回滚,
-
线程池关闭相关操作
-
shutdown
- shutdown()后线程池将变成shutdown状态,此时不接收新任务,
但会处理完正在运行的 和 在阻塞队列中等待处理的任务。
- shutdown()后线程池将变成shutdown状态,此时不接收新任务,
-
shutdownNow
- shutdownNow()后线程池将变成stop状态,此时不接收新任务,
不再处理在阻塞队列中等待的任务,还会尝试中断正在处理中的工作线程。
- shutdownNow()后线程池将变成stop状态,此时不接收新任务,
-
阻塞队列
-
支持两个附加操作
- 当队列满时,存储元素的线程会等待队列可用
- 当队列空时,获取元素的线程会等待队列变为非空
-
阻塞队列先设定大小,防止内存溢出
-
线程池队列
-
ArrayBlockingQueue(有界队列)
- 是一个基于数组结构的有界阻塞队列,
此队列按 FIFO(先进先出)原则对元素进行排序 - 可以指定缓存队列的大小,当正在执行的线程数等于corePoolSize时,多余的
元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当
ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的
线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素
尝试加入ArrayBlockingQueue时会报错
- 是一个基于数组结构的有界阻塞队列,
-
LinkedBlockingQueue(无界队列)
-
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出)
排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法
Executors.newFixedThreadPool()使用了这个队列。 -
maximumPoolSizes为无效
- 当前执行的线程数量达到corePoolSize的数量时,
剩余的元素会在阻塞队列里等待
- 当前执行的线程数量达到corePoolSize的数量时,
-
-
SynchronousQueue(同步队列)
-
一个不存储元素的阻塞队列。每个插入操作必须等到
另一个线程调用移除操作,否则插入操作一直处于阻塞状态,
吞吐量通常要高于LinkedBlockingQueue,静态工厂方法
Executors.newCachedThreadPool使用了这个队列。 -
maximumPoolSizes为无界
- 使用SynchronousQueue阻塞队列一般要求
maximumPoolSizes为无界,避免线程拒绝执行操作
- 使用SynchronousQueue阻塞队列一般要求
-
特点
- SynchronousQueue 队列中没有任何缓存的数据,
可以理解为容量为 0 - SynchronousQueue 提供两种实现方式,
分别是 栈 和 队列 的方式实现。这两种实现方式中,
栈 是属于非公平的策略,队列 是属于公平策略
- SynchronousQueue 队列中没有任何缓存的数据,
-
-
DelayQueue(延迟队列)
- 一个任务定时周期的延迟执行的队列。
根据指定的执行时间从小到大排序,
否则根据插入到队列的先后排序。
- 一个任务定时周期的延迟执行的队列。
-
PriorityBlockingQueue(优先级队列)
- 一个具有优先级得无限阻塞队列
-
LinkedTransferQueue
-
判断线程是否停止的方法interrupted
和isinterrupted的区别
-
interrupted
- 静态方法
- 作用于当前正在运行的线程
- 会清除线程中断状态
-
isinterrupted
- 非静态方法
- 作用于该方法的调用对象所对应的线程
三个线程交替顺序打印ABC
- 使用synchronized, wait和notifyAll
- 使用Lock->ReentrantLock 和 state标志
- 使用Lock->ReentrantLock 和
Condition(await 、signal、signalAll) - 使用Semaphore
- 使用AtomicInteger
多线程中join()方法
- t.join()方法只会使主线程进入等待池并等待t线程
执行完毕后才会被唤醒。并不影响同一时刻处在
运行状态的其他线程
缓存一致性问题
- 加lock锁
- 缓存一致性协议
ThreadLocal
- 相当于一个容器,用于存放每个线程的局部变量
- 底层也是封装了ThreadLocalMap集合类来绑定当
前线程和变量副本的关系,各个线程独立并且访问安全
run和start
死锁
-
必要条件
- 互斥
- 请求和保持
- 循环等待
- 不可剥夺
-
防止死锁
- 预防
- 避免
- 检测
- 解除
锁优化
-
减少锁的持有时间
-
减少锁得用于粒度
-
锁分离
- 读锁
- 写锁
-
锁粗化
- 如果一段程序要多次请求锁,锁之间的代码执行时间比较少,就应该整合成一个锁
-
自旋锁(不可重入锁)
-
锁清除
- 堆检测到不可能存在共享数据竞争的锁进行清除(逃逸分析技术)
-
锁降级
- 指的是把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前有用的)写锁的过程
volatile
-
可见性
- 底层是共享变量
-
禁止重排序
-
内存屏障
- LoadLoad
- StoreStore
- LoadStore
- StoreLoad
-
-
内部实现机制
- 将当前内核高速缓存行的数据立刻回写到内存
- 使在其他内核里缓存了该内存地址的数据无效
- MESI协议,该解决缓存一致性的思路是:当CPU写数据时,如果发现操作的变量是共享
变量,即在其他CPU中也存在该变量的副本,那么他会发出信号通知其他CPU将该变量
的缓存行设置为无效状态。当其他CPU使用这个变量时,首先会去嗅探是否有对该变量更
改的信号,当发现这个变量的缓存行已经无效时,会从新从内存中读取这个变量。
-
内存语义
-
内存可⻅性
-
具有与锁相同的内存主题,轻量级锁
- volatile仅仅保证对单个volatile变量的读/写具有原⼦
性 - 锁可以保证整个临界区代码的执⾏具有原⼦性
- 在功能上,锁⽐
volatile更强⼤;在性能上,volatile更有优势
- volatile仅仅保证对单个volatile变量的读/写具有原⼦
-
-
禁⽌重排序
-
双重锁检查的单例模式
-
JSR-133增强:严格限制编译器和处理器对volatile变量与普通变量的重排序
-
禁止编译器重排序
- 在每个volatile写操作前插⼊⼀个StoreStore屏障
- 在每个volatile写操作后插⼊⼀个StoreLoad屏障
- 在每个volatile读操作后插⼊⼀个LoadLoad屏障
- 在每个volatile读操作后再插⼊⼀个LoadStore屏障
-
禁止处理器重排序
-
内存屏障
-
读屏障(Load Barrier)
-
写屏障
(Store Barrier) -
作用
- 阻⽌屏障两侧的指令重排序
- 强制把写缓冲区/⾼速缓存中的脏数据等写回主内存,或者让缓存中相应的数据
失效,这里的缓存指CPU缓存L1,L2
-
-
-
volatile变量与普通变量的访问规则
- 子主题 1
-
-
-
用户态和核心态
无锁化编程实现线程安全
- final(数据不可修改)
- CAS
- ThreadLocal
读写锁
- 写独占,读共享,CopyOnWriteArrayList(非公平锁)
如何判断一个线程是否拥有锁
- Thread的holdsLock方法
happens-before原则
- 如果前一个操作(A)必须要对后一个操作(C)可见 ,那么这两个操作(A C) 指令不能重排
乐观锁(适用于大并发量)
-
CAS
-
Unsafe
-
不同的操作系统和处理器的实现
会有所不同 -
怎样使用:java.util.concurrent.atomic 原子操作类
- do-while循环保证循环体至少执行一遍
-
问题
-
ABA问题
- 版本号或者时间戳
- JDK1.5提供的AtomicStampedReference
-
循环时间⻓开销⼤
-
让JVM⽀持处理器提供的pause指令
pause指令能让⾃旋失败时cpu睡眠⼀⼩段时间再继续⾃旋,从⽽使得读操作的频
率低很多,为解决内存顺序冲突⽽导致的CPU流⽔线重排的代价也会⼩很多
-
-
只能保证⼀个共享变量的原⼦操作
- JDK1.5的AtomicReference
- 锁
-
-
悲观锁(适用于并发量不大的场景)
-
Lock
- 悲观锁、可中断锁、可公平锁、可重入锁)
-
synchronized
- 悲观锁、不可中断锁、非公平锁、可重入锁)
- 底层通过操作系统的互斥量实现,适用于竞争不激烈的场景
可重入锁和不可重入锁(自旋锁)
- 可重入锁实现原理:当一个线程请求成功后,JVM会记下持有锁的线程,
并将计数器计为1。此时其他线程请求该锁,则必须等待;
而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增
synchronized 和 volatile 的区别是什么?
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,
需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以
访问该变量,其他线程被阻塞住 - volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;
而synchronized则可以保证变量的修改可见性和原子性 - volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
synchronized 和 ReentrantLock 区别是什么
-
synchronized是关键字、ReentrantLock是类
- ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,
可以被继承、可以有方法、可以有各种各样的类变
- ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,
-
ReentrantLock可以对获取锁的等待时间进行设置
-
ReentrantLock可以获取各种锁的信息
-
sychronized是不可中断锁、非公平锁;ReentrantLock是可中断,可公平锁
-
ReentrantLock需在finally中手工释放锁
高并发系统限流中的算法
-
漏桶算法
- 一个固定容量的漏桶,按照固定常量速率流出请求,
流入请求速率任意,当流入的请求数累积到漏桶容量时,
则新流入的请求被拒绝
- 一个固定容量的漏桶,按照固定常量速率流出请求,
-
令牌桶算法
- 一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,
填满了就丢弃令牌,请求是否被处理要看桶中令牌是否足够,
当令牌数减为零时则拒绝新的请求
- 一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,
-
计数器限流算法
- 用来限制一定时间内的总并发数,
比如数据库连接池、线程池、秒杀的并发数
- 用来限制一定时间内的总并发数,
锁的状态
-
无锁状态
-
偏向锁状态
- 偏向锁是指一段同步代码一直被一个线程所访问,
那么该线程会自动获取锁。降低获取锁的代价。
- 偏向锁是指一段同步代码一直被一个线程所访问,
-
轻量级锁状态
- 轻量级锁是指当锁是偏向锁的时候,被另一个线程
所访问,偏向锁就会升级为轻量级锁,其他线程会
通过自旋的形式尝试获取锁,不会阻塞,提高性能。
- 轻量级锁是指当锁是偏向锁的时候,被另一个线程
-
重量级锁状态
- 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,
但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,
就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线
程进入阻塞,性能降低。
- 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,
自旋锁与非自旋锁
-
自旋锁
- 一直循环尝试获取锁
-
非自旋锁
- 阻塞等待
-
公平
-
公平的自旋锁
- https://blog.csdn.net/qq_34337272/article/details/81252853?utm_source=app
-
-
可重入
-
可重入的自旋锁
- https://blog.csdn.net/qq_34337272/article/details/81252853?utm_source=app
-
Thread类和Runnable接⼝
-
实现线程
- 继承 Thread 类,并重写 run ⽅法
- 实现 Runnable 接⼝的 run ⽅法
-
Thread类的⼏个常⽤⽅法
-
currentThread()
-
start()
-
yield()
当前线程调⽤了yield()⽅法,程序在调度的时候,也还有可能继续运⾏这个线程的
-
sleep(),睡眠一段时间
-
join()
使当前线程等待另⼀个线程执⾏完毕之后再继续执⾏,内部调⽤的是Object类的wait⽅法实现的
-
wait、notify、notifyall
- 只能由获取对象监视器的线程调用,否则抛出异常IllegalMonitorStateException
- Object类的方法
wait、sleep
-
wait
- 阻塞线程并且释放锁
- Object类
-
sleep
- 阻塞线程但不释放锁
- Thread类
锁接口与类
-
synchronized的不⾜之处
- 如果临界区是只读操作,其实可以多线程⼀起执⾏,但使⽤synchronized的
话,同⼀时间只能有⼀个线程执⾏ - synchronized⽆法知道线程有没有成功获取到锁
- 使⽤synchronized,如果临界区因为IO或者sleep⽅法等原因阻塞了,⽽当前
线程⼜没有释放锁,就会导致所有线程等待
- 如果临界区是只读操作,其实可以多线程⼀起执⾏,但使⽤synchronized的
-
锁的分类
-
可重⼊锁和⾮可重⼊锁
- synchronized是可重⼊锁
- 继承AQS类时考虑是否可重入
- ReentrantLock是可重入锁
-
公平锁与⾮公平锁
- 先来后到
- ⾮公平锁能提升⼀定的效率。但是⾮公平锁可能会发⽣线程饥饿(有
⼀些线程⻓时间得不到锁 - ReentrantLock⽀持⾮公平锁和公平锁两种
-
读写锁和排它锁
-
排它锁在同⼀时刻只允许⼀个线程进⾏访问
- synchronized锁和ReentrantLock都是排它锁
-
读写锁可以在同⼀时刻允许多个读线程访问
- ReentrantReadWriteLock
-
-
-
java.util.concurrent.locks包
-
抽象类AQS/AQLS/AOS
-
接⼝Condition/Lock/ReadWriteLock
-
Condition
-
Object 的wait/notify⽅法来实现等待/通
知机制 -
与Lock配合来
实现等待/通知机制 -
两者对比
- Condition的await⽅法对应的是
Object的wait⽅法,⽽Condition的signal/signalAll⽅法则对应Object的
notify/notifyAll()
- Condition的await⽅法对应的是
-
-
-
ReentrantLock
- Lock接口实现
-
ReentrantReadWriteLock
- ReadWriteLock接口实现
- 缺点:在“写”操作的时
候,其它线程不能写也不能读,称为“写饥饿”
-
StampedLock
-
锁中的性能之王
-
实现读写锁的功能
-
读锁
- 乐观读锁
- 悲观读锁
-
不会发生写饥饿
在读的时候如果发⽣了写,应该通过
重试的⽅式来获取新的值,⽽不应该阻塞写操作。这种模式也就是典型的⽆锁编程
思想,和CAS⾃旋的思想⼀样 -
取代ReentrantReadWriteLock
-
-
线程池
-
使用原因
- 复用线程
- 控制并发
- 统⼀管理
-
Executor 顶层接⼝
-
ThreadPoolExecutor实现类
-
四个构造函数
-
7个参数
-
corePoolSize
- 核心线程数(铁饭碗)
-
maximumPoolSize
- 最大线程数(核心+非核心)
-
keepAliveTime
- ⾮核⼼线程闲置超时时⻓,allowCoreThreadTimeOut(true)会作用于核心线程
-
unit
- keepAliveTime的单位
-
workQueue
- Runnable类型的阻塞队列
-
threadFactory
- 创建线程的⼯⼚,设置线程的参数
-
handler
-
拒绝处理策略
-
线程数量⼤于最⼤线程数就会采⽤拒绝处理策略
-
4种实现
- ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常
- ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执⾏程序(如果再次失败,重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调⽤线程处理该任务
-
-
-
execute处理流程
-
线程复用原理
-
-
ExecutorService子接口
- AbstractExecutorService抽象实现类,实现了ExecutorService子接口的全部方法,子类只要重写Excutor的execute方法即可
- 线程池的相关操作
-
void execute(Runnable command)方法
-
-
Executors 类创建线程池
-
建议
- 参考阿里巴巴Java开发手册
同步容器与并发容器
-
区别:同步容器存在线程安全的复合操作问题
-
并发Map
-
ConcurrentMap接⼝
- ConcurrentHashMap类
-
ConcurrentNavigableMap接⼝
- ConcurrentSkipListMap类
-
-
并发Queue
- ConcurrentLinkedDeque
- ConcurrentLinkedQueue
-
并发Set
-
ConcurrentSkipListSet
- 线程安全的有序集合。底层ConcurrentSkipListMap
-
谷歌guava
- ConcurrentHashSet
- Set s = Sets.newConcurrentHashSet();
-
CopyOnWriteArraySet
-
-
并发List
-
CopyOnWriteArrayList
- 用例:定时黑名单刷新
- 最终一致性
-
CopyOnWriteArrayList
-
通信⼯具类
-
Semaphore
- 限制线程的数量
-
Exchanger
-
两个线程交换数据
- exchange(V x)
- exchange(V x, long timeout, TimeUnit unit)
-
-
CountDownLatch
-
线程等待直到计数器减为0时开始⼯作
-
公共方法
// 构造⽅法:
public CountDownLatch(int count)
//等待
public void await()
public boolean await(long timeout, TimeUnit unit) // 超时等待
public void countDown() // count - 1
public long getCount() // 获取当前还有多少count -
用例:游戏开始之前的前置任务
- 加载地图数据
- 加载⼈物模型
- 加载背景⾳乐
-
缺点:不能重新计算计数值,只能起到一次屏障的作用
-
-
CyclicBarrier
- 作⽤跟CountDownLatch类似,但是可以重复使⽤
- 异常处理机制
- https://juejin.im/post/5b6fce4be51d45664c239f14
-
Phaser
- 增强的CyclicBarrier
- 可忽略
Callable、Future与FutureTask
-
Callable接⼝
- 与Runnable相比,有返回值
- 函数式接口
-
Future接口
-
顶层接口
-
ExecutorService的三个submit方法的返回值
- Future submit(Callable task)
- Future submit(Runnable task, T result)
- Future<?> submit(Runnable task)
-
cancel方法可以取消线程的执行,不一定成功
-
-
FutureTask类
- ExecutorService的三个submit方法的Future接口实现类
死锁
-
故事:哲学家进餐问题
-
有向图
- 节点为线程,边为线程等待对方线程占有的资源
- 形成环路产生死锁
-
锁顺序死锁
- 获取锁方向不同
- 有A想B,有B想A
计划任务
-
ScheduledThreadPoolExecutor
-
阻塞队列:DelayedWorkQueue
- 队头是最近将要执⾏的任务
- 优先队列,底层是数组堆
- 因为有出入队的调整,定时不是十分准确
-
构造方法调用父类
-
实现ScheduledExecutorService接口
-
schedule
- 单次任务
-
schedule
- 单次任务
-
scheduleAtFixedRate
- 第一次任务延迟,每隔一段时间,前面任务未完成则等待
-
scheduleWithFixedDelay
- 第一次任务延迟,每次完成任务等待delay再运行
-
-
执行原理
- 包装任务:RunnableScheduledFuture
- 调用void delayedExecute(RunnableScheduledFuture<?> task)
-
-
Timer
AQS
-
抽象队列同步器
- ReentrantLock
- ReentrantReadWriteLock
- Semaphore
- FutureTask
- SynchronousQueue
-
数据结构
- FIFO队列
-
资源共享模式
-
独占模式
- ReentrantLock
-
共享模式
- Semaphore/CountDownLatch
-
使用
- 选择其中一种模式
- 两种模式共用,如ReadWriteLock
-
内部类Node
注意:通过Node我们可以实现两个队列,⼀是通过prev和next实现CLH队列,⼆是nextWaiter实现Condition条件上的等待线程队列(单向队列),这个Condition主要⽤在ReentrantLock类中
-
内部类ConditionObject
-
-
AQS的主要⽅法解析
-
protected方法
-
boolean tryAcquire(int)
-
boolean tryRelease(int)
-
int tryAcquireShared(int)
-
boolean tryReleaseShared(int)
-
boolean isHeldExclusively()
- 该线程是否正在独占资源。只有⽤到condition才需要去
实现它
- 该线程是否正在独占资源。只有⽤到condition才需要去
-
volatile类型变量state
- getState
- setState
- compareAndSetState
-
-
public方法
- void acquire(int)
- void acquireInterruptibly(int)
- void acquireShared(int)
- void acquireSharedInterruptibly(int)
- boolean tryAcquireNanos(int,long)
- boolean tryAcquireSharedNanos(int, long)
- boolean release(int)
- boolean releaseShared(int)
-
线程间通信方式
-
共享变量
-
Object
- wait
- notify
- notifyall
-
Condition
- await
- signal
- signalall
-
LockSupport
等待通知
-
含义:A线程等待,B线程通知
-
实现
-
自旋
-
Object
- wait
- notify
- notifyall
-
Condition接口
Object实现的等待通知只能在synchronized中使用,当使用Lock显式锁就不能使用了,而Condition就是解决这个问题的
-
实现
-
AQS中ConditionObject实现类,底层调用LockSupport
- await前会释放锁
-
-
功能
- 分组唤醒
-
-
-
介绍:https://www.cnblogs.com/sheeva/p/6484224.html
线程状态
-
操作系统线程状态
- new
- ready
- running
- waiting
- terminated
-
Java线程状态(Thread.State)
-
NEW
-
RUNNABLE
- 操作系统线程的ready+running
-
BLOCKED
- 正等待锁的释放以进⼊同步区
-
WAITING
-
方法
- Object.wait()
- Thread.join()
- LockSupport.park()
-
RUNNABLE状态需要其他线程唤醒
-
-
TIMED_WAITING
-
线程等待⼀个具体的时间,时间到后会被⾃动唤醒
-
方法
- Thread.sleep(long millis)
- Object.wait(long timeout)
- Thread.join(long millis)
- LockSupport.parkNanos(long nanos)
- LockSupport.parkUntil(long deadline)
-
-
TERMINATED
-
XMind - Trial Version