java并发编程
2022-05-18 22:55:49 0 举报
AI智能生成
登录查看完整内容
java并发知识相关总结
作者其他创作
大纲/内容
TransferQueue
queue
LinkedBlockingDeque
BlockingDeque
deque
CopyOnWriteArrayList
CopyOnWriteArraySet
copyonwriter
ConcurrentLinkedQueue
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentSkipListSet
concurrent
为什么put/take 存储和取出时都要唤醒对立的线程
数组形式存储
ArrayBlockingQueue
put
take
LinkedBlockingQueue
PriorityBlockingQueue
单向链表.先进先出
head.tail
结构
公平
先进后出
TransferStack
非公平(默认)
原理
SynchronousQueue
DelayQueue
BlockingQueue
阻塞队列
并发集合
数组+cas
LongAdder
cas 某一个变量在极高并发的情况下cpu空转问题严重
AtomicLong
atomic
独占锁
ReentrantLock
读读不互斥
读写互斥
加锁特征
同一个线程先加写锁 再加读锁 允许 锁的降级
同一个线程先加读锁 再加写锁 不允许 锁的升级
锁的 升降级
高16位为读锁的加锁次数
低16位为写锁的加锁次数
读写锁的加锁次数最大为1<<<16 -1
state
线程唤醒
ReentrantReadWriteLock
基于时间戳
悲观读锁
乐观读
独占写
特点
使用实例
StampedLock
cancel 1
默认状态 0
signal -1
condition -2
bug
PROPAGATE -3
node状态
AbstractQueuedSynchronizer
await
signal
使用
Condition
lock
Future
Callable
便于资源管理和控制
降低创建线程和销毁线程的性能开销
提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行
优点
可调度
newScheduledThreadPool
newCachedThreadPool
单个线程
newSingleThreadExecutor
普通任务
newFixedThreadPool
forkjoin
newWorkStealingPool
标准线程池
核心线程数
coreSize
最大线程数
maximumPoolSize
最大线程与核心线程的差值线程存活时间
keepAliveTime
BlockingQueue<Runnable> workQueue
线程工厂
ThreadFactory threadFactory
抛出异常
AbortPolicy
在当前线程执行
CallerRunsPolicy
if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); }
丢弃下一个将要执行的任务
DiscardOldestPolicy
DiscardPolicy
RejectedExecutionHandler (拒绝策略)
核心参数
接受任务
RUNNING
SHUTDOWN
STOP
TIDYING
terminated()方法 执行完成
TERMINATED
线程池状态
COUNT_BITS = Integer.SIZE - 3
线程池数量
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
调用shutdown.setCoreSize(核心线程数减小) 会中断空闲线程
Worker
从阻塞队列去任务超时时间
线程释放及keeplive作用
interruptIdleWorkers
线程管理
预初始化核心线程数
prestartAllCoreThreads
完成任务数
getCompletedTaskCount
任务执行前
beforeExecute
任务执行后
afterExecute
监控
线程池管理
ThreadPoolExecutor
线程池
内存泄漏
hash冲突解决
清理范围 slotToExpunge-len
向前(循环)存在脏数据(slotToExpunge=3)向后(并非到len结束也可循环)存在可覆盖(i)的数据
向前存在脏数据向后不存在可覆盖的数据
清理范围 初始定位值-len
清理entry不为空但是key为空的数据
set hash定位逻辑
ThreadLocal
Fork-join
只有状态为New时才允许取消
New
任务执行完成 但是还未设置结果
COMPLETING
NORMAL
EXCEPTIONAL
任务取消
CANCELLED
中断线程前
INTERRUPTING
中断任务结束
INTERRUPTED
状态
单向链表WaitNode(后进先出)
阻塞线程
runnable->callable
任务完成
get
中断执行任务线程
FutureTask
AtomicReference
aba问题
ThreadLocalRandom
其他
基于ReentrantLock实现+Condition 阻塞
最后一个线程到达终点 执行command
阻塞
某一个中断唤醒所有的线程
待超时阻塞
唤醒所有处于等待的线程
breakBarrier
CyclicBarrier
读写锁一种
sate<=0 则阻塞 等待唤醒
state=0 则唤醒clh节点的线程
countDown
CountDownLatch
计数器限流
spring-cloud-hystrix 策略 默认为ThreadPool 信号量也支持
acquire
释放令牌
归还state state+permit并唤醒等待令牌的线程
countdownlatch需要state等于0才唤醒
release
Semaphore 信号量
协调组件
putIfAbsent
computeIfAbsent
数据合并
merge
方法
数组+链表+红黑树
数组长度>64
链表长度大于8
链表转换为红黑树条件
数据结构
baseCount (cas)+CountCell数组
VOLITALE 标记的数值
填充缓存行
sun.misc.Contended
CountCell
CELLSBUSY 作为cas标记位
size统计
低16位-1 代表扩容线程数量(-1为初始化中) +2
可以保证每次扩容都生成唯一的生成戳,每次新的扩容,都有一个不同的 n,这个生成戳就是根据 n 来计算出来的一个数字,n 不同,这个数字也不同
扩容戳
ForwardingNode
转移数据的索引值
TRANSFERINDEX
分段扩容已分配完毕
transferIndex
扩容结束标记
高低位迁移
数据迁移
断是否需要扩容,也就是当更新后的键值对总数 baseCount >= 阈值 sizeCtl 时,进行rehash,这里面会有两个逻辑。1. 如果当前正在处于扩容阶段,则当前线程会加入并且协助扩容2. 如果当前没有在扩容,则直接触发扩容操作
当前容量与正在扩容的容量不一致;表示比较高 RESIZE_STAMP_BITS 位生成戳和 rs 是否相等,相同
(sc >>> RESIZE_STAMP_SHIFT) != rs
扩容结束的最后一个线程会将其减1
表示扩容结束
sc == rs + 1
达到可参与最大扩容线程数
sc == rs + MAX_RESIZERS
表示所有的 transfer 任务都被领取完了,没有剩余的hash 桶来给自己自己好这个线程来做 transfer
transferIndex <= 0
表示扩容已经结束
(nt = nextTable) == null
此值计算出来始终保证该值左移16位时最高位为1 ,为负数
Integer.numberOfLeadingZeros(n) 由于n越大 最高位非0位0的个数始终在减少, 最大值为16对应的值为27 与(1<<15)计算后最大值 为32795 ,每次扩容就依次减少, 所以rs的高16位始终为0
rs=Integer.numberOfLeadingZeros(n) | (1 << (16 - 1))
是否参与扩容判断
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE;
步长分配
并发协助扩容
(h ^ (h >>> 16)) & HASH_BITS
hash值计算
-1 代表数组初始化
(rs << RESIZE_STAMP_SHIFT) + 2
>=0 说明未初始化容量或者阈值
sizeCtl
约束
2 jdk1.7最大并发粒度为16
4 jdk1.7链表采用头插法 jdk1.8 采用尾插法
比较
0 初始值
1 contcell 在扩容或者初始化
缓存行填充
@sun.misc.Contended static final class CounterCell { volatile long value; CounterCell(long x) { value = x; } }
疑问点
CELLSBUSY
volatile 的数组只针对数组的引用具有volatile 的语义,而不是它的元素
拓展知识点
chm扩容分析
chm
底层实现
compare and swap (cpu层面的原子指令)
native中存在4个参数
cpu空转
只能保证一个共享变量原子操作
缺陷
cas
线程切换 线程执行过程中不被中断
抢占式
轮询
调度算法
原子性源头
伪共享: 当一个CPU高速缓存行发生改变,为了不影响其他CPU的相关数据的正确性,必须通过某些手段让其他拥有该缓存行数据的CPU高速缓存进行数据一致性同步,这就是造成“伪共享”的所在,试想运行在两个不同CPU核心上的两个线程,如果他们操作的内存数据在物理上处于相同或相邻的内存块区域(或者干脆就是操作的相同的数据),那么在将它们各自操作的数据加载到各自的一级缓存L1的时候,在很大概率上它们刚好位于一个缓存行(这是很有可能的,毕竟一个缓存行的大小一般有64个字节),即共享一个缓存行,这时候只要其中一个CPU核心更改了这个缓存行,都会导致另一个CPU核心的相应缓存行失效,如果它们确实操作的是同一个数据变量(即共享变量)这无可厚非,但如果它们操作的是不同的数据变量呢,依然会因为共享同一个缓存行导致整个缓存行失效,不得不重新进行缓存一致性同步,出现了类似串行化的运行结果,严重影响性能,这就是所谓的“伪共享”
@sun.misc.Contended
解决办法
缓存行伪共享
cpu缓存行 大小64字节
cpu缓存行
L1 独占
L2 独占
L3 共享
cpu缓存
可见性源头
指令重排序
有序性源头
如果一个存储器的位置被引用,那么将来他附近的位置也会被引用
空间局部性
被引用过一次的存储器位置在未来会被多次引用(通常在循环中)
时间局部性
局部性原理(在CPU访问寄存器时,无论是存取数据抑或存取指令,都趋于聚集在一片连续的区域中,这就被称为局部性原理)
并发基础
共享内存
消息传递
线程间通讯
可见性
禁止指令重排序
作用
总线上lock锁
STORE BUFFER
store forwarding
invalid queue
缓存一致性协议
MESI 缓存一致性协议
缓存锁
仅在32位系统上对long.double的读写具有原子性
原子性
特性
该屏障确保Store1立刻刷新数据到内存(使其对其他处理器可见)的操作先于Store2及其后所有存储指令的操作
Store1;StoreStore;Store2
storestore
store1 ;storeload;load2
storeload
该屏障确保Load1数据的装载先于Load2及其后所有装载指令的的操作
Load1;LoadLoad;Load2
loadload
确保Load1的数据装载先于Store2及其后所有的存储指令刷新数据到内存的操作
Load1;LoadStore;Store2
loadstore
规范
Store Barrier,相当于StoreStore Barriers。
sfence
Load Barrier,相当于LoadLoad Barriers。
lfence
Full Barrier ,相当于StoreLoad Barriers。
mfence
x86实现
如果一个实例的字段被声明为final,则JVM会在初始化final变量后插入一个sfence
final
内存屏障
实现机制
读
写
内存语义
在处理器内核中一般会有多个执行单元,比如算术逻辑单元、位移单元等。在引入并行指令集之前,CPU在每个时钟周期内只能执行单条指令,也就是说只有一个执行单元在工作,其他执行单元处于空闲状态;在引入并行指令集之后,CPU在一个时钟周期内可以同时分配多条指令在不同的执行单元中执行。
对于一条从内存中读取数据的指令,CPU的某个执行单元在执行这条指令并等到返回结果之前,按照CPU的执行速度来说它足够处理几百条其他指令,而CPU为了提高执行效率,会根据单元电路的空闲状态和指令能否提前执行的情况进行分析,把那些指令地址顺序靠后的指令提前到读取内存指令之前完成
并行指令集重排序
内存系统指令重排序
优化点
重排序规则
在volatile写前面添加store store指令
在volatile写后面添加storeload 指令
volatile写
volatile读后添加LoadLoad指令
volatile读后添加LoadStore指令
volatile 读
jmm对volatile变量操作规约
volitale语义
程序的顺序性规则
传递性规则
管程锁规则
线程join规则
线程start规则
happens-before
单线程执行结果不被改变
as-if-serial
程序有在一段时间内多次访问同一个数据块的倾向
程序往往有访问一个聚集空间的数据块的倾向,参见数组的访问
计算机操作系统中的局部性原理
内存模型
NEW
RUNNABLE
join.sleep.wait.await
LockSupport.park() 不会抛出中断异常 但会唤醒等待的线程
WAITING
TIMED_WAITING
synchronized
BLOCK
线程状态
interrupt
interrupted
示例:
Thread.cpp 源码
调用的线程从runnable进入waiting状态
调用wait()
join
线程中断
线程优先级
线程run方法结束
LockSupport.park
LockSupport.unpark
syncronized
系统中断
多个线程抢占synchronized 同步锁资源
时机
线程挂起
线程恢复
cpu时时间片分配
原因
cas指令 cpu层面的指令 不会出现线程中断
vmstat 中的cs(content switch 每秒上下文切换次数) 值越小越好
查看命令
什么是上下文
为什么会发生上下文切换
线程上下文切换
sleep不释放锁.wait释放锁
sleep.wait区别
线程基础
标准
ifence
mfence(full fence)
操作系统实现
JMM
锁粗化
锁消除
无锁
偏向锁
轻量级锁
重量级锁
锁的升级
共享资源 X 和 Y 只能被一个线程占用;
互斥
线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
占有且等待
其他线程不能强行抢占线程 T1 占有的资源
不可抢占
线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
循环等待
产生的死锁的四个条件
我们可以一次性申请所有的资源
占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。
破坏条件
死锁
休眠随机时间
活锁
锁饥饿
问题
悲观锁
乐观锁
是否阻塞
共享锁
排他锁
多个线程能不能共享一把锁
可重入锁
非可重入锁
一个线程中的多个流程能不能获取同一把锁
同一个线程执行同步资源时自动获取资源
公平锁
非公平锁
抢占锁是否排队
自旋
适应性自旋
不阻塞
抢占同步资源失败是否阻塞
锁的分类
锁相关
单例模式
重排序
dcl
禁止重排序
volitale 关键字
解决方案
原因
DCL
java并发编程
0 条评论
回复 删除
下一页