文章目录
重点词汇:
锁:synchronized、ReentrantLock、Semaphore、Atomic原子类
关键字:volatile、CountDownLatch、CyclicBarrier
3.1 Java线程的创建方式
Thread类本身已经实现了Runnable接口。
Thread对象的run方法中其实调用了Runnable对象的run方法。
3.2 线程池的工作原理
3.2.1 线程复用
3.2.2 线程池的核心组件和核心类
核心组件:
- 线程池管理器:用于创建并管理线程池。
- 工作线程:线程池中执行具体任务的线程。
- 任务队列:存放待处理的任务,新的任务将会不断被加入队列中,执行完成的任务将被从队列中移除。
- 任务接口:用于定义工作线程的调度和执行策略,只有线程实现了该接口,线程中的任务才能够被线程池调度。
核心类:ThreadPoolExecutor、Executors、ExecutorService、Executor、Callable、Future、FutureTask
3.2.3 Java线程池的工作流程
3.2.4 线程池的拒绝策略
- CallerRunsPolicy:调用者线程直接执行该线程任务。
- DiscardPolicy:直接丢弃该线程任务。
- DiscardOldestPolicy:丢弃最早的一个线程任务。
- AbortPolicy:抛出异常。
- 自定义拒绝策略
3.3 5种常用的线程池
3.4 线程的生命周期
3.5 线程的基本方法
- 新建:new
- 新建->就绪:start
- 运行->就绪:yield
- 运行->阻塞:sleep、suspend、wait、join
- 阻塞->就绪:resume、notify、notifyAll
- 运行->死亡:run、call、stop、interrupt、interrupted、isInterrupted、setDaemon
sleep() | suspend() | wait() |
---|---|---|
属于Thread类 | 属于Thread类 | 属于Object类 |
不释放锁 | 不释放锁 | 释放锁 |
到期自动恢复 | 调用resume()恢复 | 到期自动恢复;调用notify()/notifyAll()恢复 |
interrupt | isInterrupted | interrupted |
---|---|---|
添加中断标识 | 查看中断标识 | 查看中断标识并清除 |
操作调用方法的线程 | 操作调用方法的线程 | 操作当前代码所在的线程 |
3.7 线程上下文切换
- 上下文:指线程切换时CPU寄存器和程序计数器所保存的当前线程的信息。
- 上下文切换:CPU利用时间片轮询来为每个任务都服务一定的时间,然后把当前任务的状态保存下来,继续服务下一个任务。任务的状态保存及再加载就叫作线程的上下文切换。
3.12 Java中的线程调度
- 抢占式调度:线程有同样的CPU时间片:一个线程的堵塞不会导致整个进程阻塞。
- 协同式调度:线程自身控制CPU时间片:一个线程的堵塞将会导致整个进程阻塞。
3.13 进程调度算法
3.13.1 优先调度算法
- 先来先服务调度算法
- 短作业优先调度算法
3.13.2 高优先权优先调度算法
- 非抢占式优先调度算法
- 抢占式优先调度算法
- 高响应比优先调度算法
响应比=(等待时间+服务时间)/服务时间
3.13.3 时间片的轮转调度算法
- 时间片轮转调度算法
- 多级反馈队列调度算法:流程如下
1、假设有N个队列(Q1,…,QN),优先级Priority(Q1) > … > Priority(QN)。也就是说,只有上一级队列为空时,才会调度下一级队列。
2、每级队列都分配时间片,优先级越低的队列分配的时间片越长。
3、除了优先级最低的队列,其余队列遵循的是先来先服务算法,如果在时间片内进程未结束则将进程存入下一级队列的末尾。对于优先级最低的队列,遵循时间片轮转调度算法,如果在时间片内进程未结束则将进程存入本级队列的末尾。
3.13.4 总结
非抢占式 | 抢占式 |
---|---|
非抢占式的高优先权优先 | 抢占式的高优先权优先 |
短时间优先 | 短剩余时间优先 |
先来先服务 | 时间片轮转 |
高响应比优先 | 多级反馈队列 |
3.14 什么是CAS
3.14.1 CAS的概念:比较并交换
CAS(Compare And Swap)指比较并交换。
CAS算法CAS(V,E,N)包含 3个参数,V表示要更新的变量,E表示预期的值,N表示新值。
在且仅在 V值等于 E值时,才会将V值设为 N;如果 V值和 E值不同,则说明已经有其他线程做了更新,当前线程什么都不做。最后,CAS返回当前V的真实值。
3.14.2 CAS的特性:乐观锁
在有多个线程同时使用CAS操作一个变量时,只有一个会胜出并成功更新,其余均会失败。失败的线程不会被挂起,仅被告知失败,并且允许再次尝试,当然,也允许失败的线程放弃操作。
3.14.3 CAS自旋等待
循环地进行CAS操作:失败则重试,成功则返回结果。
3.15 ABA问题
解决方法:使用版本号。
3.16 什么是AQS
AQS(Abstract Queued Synchronizer)是一个抽象的队列同步器,通过维护一个共享资源状态(Volatile Int State)和一个先进先出(FIFO)的线程等待队列来实现一个多线程访问共享资源的同步框架。
3.16.1 AQS的原理
3.16.2 state:同步状态
state的访问:getState()、setState()和compareAndSetState(),均是原子操作。
3.16.3 AQS共享资源的方式:独占式和共享式
自定义同步器的主要方法如下:
独占式:ReentrantLock
ReentrantLock对AQS的独占方式实现为:ReentrantLock中的state初始值为 0时表示无锁状态。在线程执行tryAcquire()获取该锁后ReentrantLock中的state+1,这时该线程独占ReentrantLock锁,其他线程在通过tryAcquire()获取锁时均会失败,直到该线程释放锁后state再次为 0,其他线程才有机会获取该锁。该线程在释放锁之前可以重复获取此锁,每获取一次便会执行一次state+1,因此ReentrantLock也属于可重入锁。但获取多少次锁就要释放多少次锁,这样才能保证state最终为 0。如果获取锁的次数多于释放锁的次数,则会出现该线程一直持有该锁的情况;如果获取锁的次数少于释放锁的次数,则运行中的程序会报锁异常。
共享式:CountDownLatch
CountDownLatch对AQS的共享方式实现为:CountDownLatch将任务分为 N个子线程去执行,将state也初始化为 N,N与线程的个数一致,N个子线程是并行执行的,每个子线程都在执行完成后countDown()一次,state会执行CAS操作并减 1。在所有子线程都执行完成(state=0)时会unpark()主线程,然后主线程会从await()返回,继续执行后续的动作。
3.6 Java中的锁
- 乐观锁CAS、悲观锁AQS
- 共享锁、独占锁
- 读锁、写锁
- 公平锁、非公平锁
- 偏向锁->轻量级锁->重量级锁
- 自旋锁
- 可重入锁
重量级锁:大量线程竞争锁时,线程在就绪、运行、阻塞之间切换时,线程也会在用户态与内核态之间切换,开销大效率低。
轻量级锁:少量线程竞争锁时,使用自旋锁,线程不进入阻塞状态,直接等待持有锁的线程释放锁后获取锁,避免线程在用户态与内核态之间切换。
偏向锁:不存在锁竞争关系时,单一线程在使用重入锁的时候,消除锁重入的造成开销,只执行一次CAS原子操作。
3.6.1 synchronized
悲观锁、独占锁、非公平锁、重量级锁、自旋锁、可重入锁
内部区域:ContentionList、EntryList、Ondeck、Owner、!Owner、WaitSet
3.6.2 ReentrantLock
lock()、unLock()
悲观锁、独占锁、读锁/写锁、公平锁/非公平锁、自旋锁、可重入锁
避免死锁:轮询锁、定时锁、中断锁
3.6.3 Semaphore
acquire()、release()
悲观锁、公平锁/非公平锁
避免死锁:轮询锁、定时锁、中断锁
3.6.4 Atomic原子类
- 原子普通类:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference
- 原子数组类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 原子字段更新器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
- 带版本号的原子引用类:AtomicStampedReference、AtomicMarkableReference
- 原子累加器:LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator、Striped64
3.6.17 如何进行锁优化
- 去掉不必要的锁
- 精简锁持有的时间
- 粗化或细化锁的粒度
- 按功能分离锁(读写锁)
3.9 Java并发关键字
- CountDownLatch:计数器、不可重用。latch.await()、latch.countDown()。
- CyclicBarrier:循环屏障、可重用。cyclicBarrier.await()、cyclicBarrier.await(long timeout, TimeUnit unit)。
- volatile:volatile变量。因为使用该修饰符并不能帮助实现原子性,所以使用时需要满足两个条件:写操作不依赖于当前值;写操作不依赖于其他被volatile修饰的值。
3.10 多线程如何共享数据
可见性 | 有序性 | 原子性 | |
---|---|---|---|
synchronized | √ | √ | √ |
volatile | √ | √ | × |
3.8 Java阻塞队列
略。
3.11 ConcurrentHashMap并发
略。