【多线程与高并发】知识点汇总
程序员文章站
2022-03-09 19:25:50
线程的概念、启动方式、常用方法继承Thread实现Runnable接口实现Callable接口synchronized(Object)- 不能用String、常量、Integer、Long- "object"线程同步- synchronized锁的是对象不是代码this、XX.class锁定方法、非锁定方法可同时执行锁升级 - 偏向锁、自旋锁、重量级锁偏向锁:没有......
线程的概念、启动方式、常用方法
-
继承Thread
-
实现Runnable接口
-
实现Callable接口
-
synchronized(Object)
- 不能用String、常量、Integer、Long
- "object"
-
线程同步
- synchronized
-
锁的是对象不是代码
-
this、XX.class
-
锁定方法、非锁定方法可同时执行
-
锁升级
- 偏向锁、自旋锁、重量级锁
偏向锁:没有真正加锁
自旋锁:在CPU,占用CPU时间,适用于线程数量少、执行时间短的场景
重量级锁: 在OS(操作系统),不占用CPU时间,适用于数量多、执行时间长的场景
-
volatile
-
保证线程可见性
-
- MESI
- 缓存一致性协议
-
禁止指令重排序
- DCL单例
- Double Check Lock
- Mgr06.java
CAS(无锁优化 自旋)
-
Compare And Set
-
cas(V,Expected,NewValue)
- if V == E
V = New
otherwise try again or fail
- CPU原语支持,不会被打断
-
ABA问题
- 加version(版本号),AtomicStampedReference解决该问题
- A 1.0
- B 2.0
- A 3.0
- cas(version)
- 如果基础类型,无所谓;引用类型,你的女朋友跟你复合,中间经历了别的男人,就出问题了。
Unsafe
-
直接操作内存
- allocateMemory puXX freeMemory pageSize
-
直接生成类实例
- allocateInstance
-
直接操作类或实例变量
- objectFieldOffset
- getInt
- getObject
-
CAS相关操作
- weakCompareAndSetObject Int Long
JUC包中的同步锁
-
Lock接口
-
ReadWriteLock接口
-
Condition接口
-
ReentrantLock独占锁
-
ReentrantReadWriteLock读写锁
-
CountDownLatch
-
CyclicBarrier
-
Semaphore
-
AbstractOwnableSynchronizer抽象类
-
AbstractQueuedSynchronizer抽象类
-
AbstractQueuedLongSynchronizer抽象类
ReentrantLock
-
公平锁、非公平锁
-
队列,如:生产者线程、消费者线程,分别放入两个队列
ReentrantLock vs synchronized
-
ReentrantLock手动加锁、解锁;synchronized系统加锁、解锁
-
ReentrantLock有各种condition(等待队列);synchronized没有队列
-
ReentrantLock底层为AQS实现;synchronized默认实现了4种锁状态的升级
ReadWriteLock
-
读锁-共享锁
-
写锁-排他锁
LockSupport
-
一个线程阻塞工具类
-
所有的方法都是静态方法
-
不使用锁的情况下,让线程也能在任意位置阻塞、唤醒
AQS(CLH)
-
Template Method
Callback Function
父类默认实现
子类具体实现
里面运用了大量的CAS操作
-
Volatile state
- 具体由子类实现
- 双向链表,节点node,node里面是thread
-
VarHandle (JDK 9)
- 普通的属性也能变成原子操作,保证了线程安全
- 比反射快,直接操纵二进制码
ThreadLocal
-
set
- Thread.currentThread.map(ThreadLocal, person)
- 设到了当前线程的map中
-
用途
- 声明式事务,保证同一个Connection
四大引用-强软弱虚
-
强引用
- 只要有一个引用指向它(堆),垃圾回收器就不会回收它;应用:普通引用
-
软引用
- 当内存不够用时,系统会垃圾回收,先回收一次,如果不够,会把软引用干掉;应用:缓存
-
弱引用
- 应用:ThreadLocal,用完之后一定要remove掉
- 为什么Entry要使用弱引用?若是强引用,即使tl=null,但key的引用依然指向ThreadLocal对象,所以会有内存泄露,而使用弱引用则不会;但还是有内存泄露存在,ThreadLocal被回收,key的值变成null,则导致整个value再也无法被访问到,因此依然存在内存泄露
-
虚引用
- 管理堆外内存;应用:给开发JVM的人员使用,当使用虚引用的对象被回收时,通过Queue可以检测到,然后JVM开发人员可以清理堆外内存
多线程容器
-
Vector (自带锁)、Hashtable(基本不用)
-
Hashtable -> ConcurrentHashMap
-
Vector -> Queue
- CopyOnWriteList、ConcurrentHashMap、ConcurrentSkipListMap
- Queue List
- 对线程友好的API offer peek poll
- BlockingQueue
- put take -> 阻塞
-
DelayQueue、SynchronousQueue、TransferQueue
线程池
-
Executor接口
-
ExecutorService接口
-
AbstractExecutorService接口
-
-
Callable接口
-
Callable -> Runnable + ret(返回值)
-
Future -> 存储执行的将来才会产生的结果
-
FutureTask -> Future + Runnable
-
-
ThreadPoolExecutor
-
corePoolSize 核心线程数
-
maxPoolSize 最大线程数
-
keepAliveTime 存活时间
-
TimeUnit 时间单位
-
BlockingQueue 任务队列
-
ThreadFactory 线程工厂
-
RejectStrategy 拒绝策略
-
Abort 报异常
-
Discard 悄悄扔掉
-
DiscardOld 扔掉队列开头的一个
-
CallerRuns 谁调用谁执行
-
-
源码分析
-
Work类
-
Runable AQS
-
thread
-
-
submit方法
-
execute
-
core、queue、noncore
-
-
addWorker
-
count++、addWorker start
-
-
-
-
ForkJoinPool
-
分解汇总的任务
-
用很少的线程可以执行很多的任务(子任务)ThreadPoolExecutor做不到先执行子任务
-
CPU密集型
-
Executors - 线程池的工厂
-
SingleThreadExecutor
-
为什么要有单线程的线程池? 任务队列、生命周期管理
-
-
CachedThreadPool vs FixedThreadPool
-
CachedThreadPool适用于流量不稳定的情况;FixedThreadPool适用于流量比较固定的情况,可使用并行处理
-
阿里都不用,自己估算,进行精确定义
-
-
Scheduled
-
定时任务线程池
-
quartz cron框架
-
面试:假如提供一个闹钟服务,订阅这个服务的人特别多,10亿人,怎么优化?
-
- 主服务器把这些任务同步到边缘服务器,在每台服务器上用线程池加上队列
concurrent vs parallel
-
并发是指任务提交,并行指任务执行
-
并行是并发的子集,多个CPU同时处理任务
JMH,主要是测试开发人员使用
-
Java Microbenchmark Harness
-
2013年首发
-
由JIT的开发人员开发
-
归于OpenJDK
-
Disruptor
-
并发编程框架
-
特点
-
Disruptor是数组实现的
-
无锁,高并发,使用环形Buffer,直接覆盖(不用清除)旧的数据,降低GC频率
-
实现了基于事件的生产者消费者模式(观察者模式)
-
-
核心: RingBuffer
-
环形队列
-
RingBuffer的序号,指向下一个可用的元素
-
采用数组实现,没有首尾指针
-
对比ConcurrentLinkedQueue,用数组实现的速度更快
-
假如长度为8,当添加到第12个元素的时候在哪个序号上呢?用12%8决定
-
当Buffer被填满的时候到底是覆盖还是等待,由Producer决定
-
长度设为2的n次幂,利于二进制计算,例如:12%8 = 12 &(8 - 1),pos = num & (size - 1)
-
-
-
开发步骤
-
定义Event - 队列中需要处理的元素
-
定义Event工厂,用于填充队列
-
这里牵扯到效率问题:disruptor初始化的时候,会调用Event工厂,对ringBuffer进行内存的提前分配,GC产生频率会降低
-
-
定义EventHandler(消费者),处理容器中的元素
-
-
ProducerType生产者线程模式
-
ProducerType有两种模式,Producer.MULTI 和 Producer.SINGLE
-
模式是MULTI,表示在多线程模式下产生sequence
-
如果确认是单线程生产者,那么可以指定SINGLE,效率会提升
-
如果是多个生产者(多线程),但模式指定为SINGLE,会出什么问题呢?数据被覆盖
-
-
等待策略
-
BlockingWaitStrategy:通过线程阻塞的方式,等待生产者唤醒,被唤醒后,再循环检查依赖的sequence是否已经消费
-
BusySpinWaitStrategy:线程一直自旋等待,可能比较耗CPU
-
LiteBlockingWaitStrategy:线程阻塞等待生产者唤醒,与BlockingWaitStrategy相比,区别在signalNeeded.getAndSet,如果两个线程同时访问,一个访问waitfor,一个访问signalAll时,可以减少lock加锁次数
-
LiteTimeoutBlockingWaitStrategy:与LiteBlockingWaitStrategy相比,设置了阻塞时间,超过时间后抛异常
-
PhasedBackoffWaitStrategy:根据时间参数和传入的等待策略来决定使用哪种等待策略
-
TimeoutBlockingWaitStrategy:相对于BlockingWaitStrategy来说,设置了等待时间,超过后抛异常
-
YieldingWaitStrategy:尝试100次,然后Thread.yield()让出CPU
-
SleepingWaitStrategy:sleep
-
-
消费者异常处理
-
默认:disruptor.setDefaultExceptionHandler()
-
覆盖:disruptor.handleExceptonFor().with()
-
本文地址:https://blog.csdn.net/ChinaLiaoTian/article/details/109967969
上一篇: 汇编基础程序编写教程示例
下一篇: 酷狗音乐怎么去重?酷狗歌曲去重方法介绍