多线程使用笔记
2021-08-15 17:27:22 22 举报
AI智能生成
多线程运用方法
作者其他创作
大纲/内容
线程创建方式(四种)
继承Thread类
实现Runable接口
实现Calable接口+FutureTask
线程池创建
线程不安全
举例
12306抢票必须要线程安全
概念
多个线程同时访问一个资源时,由于线程执行不可控因素,可能导致最终结果与实际期望值不同
解决
加锁
synchronized
使用方式
工作原理
Lock
ReentrantLock,底层基于AQS
CAS(compare and set)
原子类,如:AtomicInteger
ConcurrentHashMap
分段加锁(jdk1.8以前),将一个数组拆分为多个数组
put操作时,采用CAS策略
hash冲突时,对链表或红黑树加了synchronized(数组[1])
CAS(compareAndSwap)
概念:比较当前工作内存中的值和主内存的值,如果相同则执行规定操作,
否则继续比较直到主内存和工作内存中的值一致为止!
否则继续比较直到主内存和工作内存中的值一致为止!
具体操作:
Unsafe类保证了原子性
自旋操作,do() while{}
底层汇编执行
总结:
自旋操作,循环时间长,可能带来CPU很大开销
不用加锁
只能保证一个共享变量的原子操作
会出现ABA问题
增加版本号处理(AtomicStampedReference)
线程池使用
核心参数(ThreadPoolExecutor)
corePoolSize
线程池中始终有的线程数量
maximumPoolSize
线程池中允许的最大线程数
keepAliveTime
当线程数大于核心线程数的时候,
线程在最大多长时间内没有接到新任务就会终止释放
最终线程池维持在核心线程池(corePoolSize)大小
线程在最大多长时间内没有接到新任务就会终止释放
最终线程池维持在核心线程池(corePoolSize)大小
unit
时间单位
workQueue
阻塞队列,用来存储等待执行的任务,|
若当前对线程的需求超过了corePoolSize大小
就会存放在此,等待空闲线程执行!
若当前对线程的需求超过了corePoolSize大小
就会存放在此,等待空闲线程执行!
threadFactory
创建线程的工厂
handler
拒绝策略,若线程池满了,线程池会使用拒绝策略
运行流程
线程池创建,准备好 core 数量的核心线程,准备接受任务
新的任务进来,用 core 准备好的空闲线程执行
core 满了,就将再进来的任务放入阻塞队列中。
空闲的 core 就会自己去阻塞队
列获取任务执行
空闲的 core 就会自己去阻塞队
列获取任务执行
、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
max 都执完后,(max-core) 数量空闲的线程会在 keepAliveTime 指定的时间后自
动销毁。最终保持到 core 大小
动销毁。最终保持到 core 大小
如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策
略进行处理
略进行处理
所有的线程创建都是由指定的 factory 创建的。
常见四种线程池
newCacheThreadPool
newFixedTheadPool
newScheduledThreadPool
newSingleThreadExecutor
为啥要用线程池
降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务
的状态,当任务来时无需创建新的线程就能执行
的状态,当任务来时无需创建新的线程就能执行
提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来
的系统开销。
无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
的系统开销。
无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
异步
CompletableFuture 异步编排
创建异步对象
runAsync(Runnable runnable)
runAsync(Runnable runnable,Executor executor)
supplyAsync(Supplier<U> supplier)
supplyAsync(Supplier<U> supplier,Executor executor)
计算完成时回调方法
、handle 方法
线程串行化方法
两任务组合 - 都要完成
两任务组合 - 一个完成
多任务组合
ThreadLocal
共享session、cookie
时间类格式化使用
JUC
可重入锁
显式锁
隐式锁
线程等待唤醒方式,必须先获得并持有锁
wait()和notify()
必须先等待后唤醒
必须结合synchronized使用
await()和signal()
必须结合Lock锁使用
必须先等待后唤醒
LockSupport
概念:阻塞唤醒
使用
park()-阻塞
unpark()-唤醒
总结
正常+无锁块要求!
无需先等待后唤醒都可以!
线程阻塞需要消耗凭证,凭证只能发放一次!
AQS(AbstractQueuedSynchronizer)
产生背景
CountDownLatch
ReentrantLock
Semaphore
ReentrantReadWriteLock
CyclicBarrier(自旋)
设计思想:acquire和release操作
同步器状态变更
CAS指令实现compareAndSetState保证原子性
volatile int state保证可见性、有序性
线程的阻塞与释放
LockSupport.park
LockSupport.unpark
CLH(链表队列)
入队操作
出队操作
内部数据结构
volitile int state
LockSupport类
Node节点
ConditionObject
主要方法及作用
boolean compareAndSetState(int expect, int update)
boolean tryAcquire(int arg)
boolean tryRelease(int arg)
int tryAcquireShared(int arg)
boolean tryReleaseShared(int arg)
boolean isHeldExclusively()
void acquire(int arg)
void acquireInterruptibly(int arg)
ConditionObject Node firstWaiter
条件队列首节点
ConditionObject Node lastWaiter
条件队列尾节点
AQS应用
ReentrantLock类使用AQS同步状态来保存锁重复持有的次数
ReentrantReadWriteLock类使用AQS同步状态中的16位来保存写锁持有的次数,
剩下的16位用来保存读锁的持有次数
剩下的16位用来保存读锁的持有次数
Semaphore类(信号量)使用AQS同步状态来保存信号量的当前计数
CountDownLatch类使用AQS同步状态来表示计数
FutureTask类使用AQS同步状态来表示某个异步计算任务的运行状态(初始化、运行中、被取消和完成)
SynchronousQueues类使用了内部的等待节点,这些节点可以用于协调生产者和消费者
0 条评论
下一页