并发编程脑图
2024-02-21 18:00:43 0 举报
AI智能生成
并发
作者其他创作
大纲/内容
并发集合
BlockingQueue
常见4种
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
DelayQueue
使用场景
缓存系统的设计
定时任务调度
SynchronousQueue
应用场景
在线程池中有比较多的应用,生产者消费者场景
工作原理
基于ReentrantLock保证线程安全
根据Condition实现队列满时的阻塞
BlockingQueue API
源码阅读
ConcurrentHashMap
什么是HashMap
高并发下的HashMap
ConcurrentHashMap结构
CopyOnWrite
CopyOnWriteArrayList
写时复制
获取–修改–写入三个步骤不是原子性
独占锁保证修改数据时只有一个线程能够进行
场景
并发容器用于读多写少的并发场景。比如白名单,黑名单
优点
读取是完全不用加锁
写入也不会阻塞读取操作
写入的同时进行读取
缺点
占用内存
弱一致性
线程基础
线程几种状态
并发工具类
CyclicBarrier
CountDownLatch
Semaphore
线程池
什么时候使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大
线程池优势
重用存在的线程,减少线程创建,消亡的开销,提高性能
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。
ThreadPoolExecutor
七大参数
corePoolSize
maximumPoolSize
keepAliveTime
unit
workQueue
threadFactory
handler
AbortPolicy
CallerRunsPolicy
DiscardOldestPolicy
DiscardPolicy
线程池流程讲解
种类
FixedThreadPool
SingleThreadExecutor
CachedThreadPool
ScheduledExecutorService
场景
redis分布式锁
采用定时线程池,假设5秒钟过期。每隔2秒钟去定时续约(续命)一次。看一下redis锁在不在,如果还在则延迟,直到逻辑执行完释放。因为不确定业务逻辑执行多少时间。
看门狗
注册中心Eureka
ThreadLocal
原子操作
基本类型
AtomicInteger
AtomicBollean
AtomicLong
数组
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
原子更新字段类
AtomicStampedReference
引用类型
操作系统底层整认识
CPU缓存结构
三级缓存结构
存储器存储空间大小:内存>L3>L2>L1>寄存器;
存储器速度快慢排序:寄存器>L1>L2>L3>内存;
CPU为何要有高速缓存
时间局部性
如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。
空间局部性
如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。
CPU运行安全等级
Linux与Windows只用到了2个级别:ring0、ring3
JVM创建线程CPU的工作过程
CPU从ring3切换ring0创建线程
创建完毕,CPU从ring0切换回ring3
线程执行JVM程序
线程执行完毕,销毁还得切会ring0
操作系统内存管理
用户态
内核态
JMM(Java内存模型)
什么是JMM
JVM运行程序的实体是线程
每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),存储线程私有的数据
Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域
堆
方法区
但线程对变量的操作(读取赋值等)必须在工作内存中进行,私有区域
程序计数器
虚拟机栈
本地方法栈
因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成
线程安全问题
如果存在两个线程同时对一个主内存中的实例对象的变量进行操作
数据同步
八大原子操作
lock(锁定)
unlock(解锁)
read(读取)
load(载入)
use(使用)
assign(赋值)
store(存储)
write(写入)
解释
如果要把一个变量从主内存中复制到工作内存中,就需要按顺序地执行read和load操作
如果把变量从工作内存中同步到主内存中,就需要按顺序地执行store和write操作
原子性,有序性、可见性
原子性
原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响
可见性
当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值
有序性
有序性是指对于单线程的执行代码,我们总是认为代码的执行是按顺序依次执行的
JMM如何解决
原子性
synchronized,Lock
保证任一时刻只有一个线程访问该代码块
可见性
volatile关键字保证可见性
当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,
即修改的值立即更新到主存中,当其他线程需要读取时,它会去主内存中读取新值。
synchronized和Lock也可以保证可见性
有序性
volatile
synchronized和Lock
happens-before 原则
as-if-serial语义
不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。
指令重排序
volatile内存语义
保证被volatile修饰的共享变量对所有线程总数可见的
禁止指令重排序优化
内存屏障
保证特定操作的执行顺序
保证某些变量的内存可见性
禁止重排优化的例子
单例模式双重检测锁
instance = new DoubleCheckLock();可以分为以下3步完成(伪代码)
memory = allocate();//1.分配对象内存空间
instance(memory);//2.初始化对象
instance = memory;//3.设置instance指向刚分配的内存地址,此时instance!=null
instance(memory);//2.初始化对象
instance = memory;//3.设置instance指向刚分配的内存地址,此时instance!=null
由于步骤1和步骤2间可能会重排序,如下:
memory=allocate();//1.分配对象内存空间
instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
instance(memory);//2.初始化对象
instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
instance(memory);//2.初始化对象
线程安全问题
volatile,private volatile static DoubleCheckLock instance;
synchronized
通过内部对象Monitor(监视器锁)实现
基本使用
每个同步对象都有一个自己的Monitor(监视器锁)
锁的膨胀升级过程
只有1个线程执行,一上来没必要去加互斥量,因为不会产生竞争。如果有两个线程,且竞争不是很激烈,就是有个线程持有锁不到1秒释放了,存在多个线程交替执行。
假设线程T0拿到了锁,线程T1,T2不会阻塞,而是去自旋,那么T0执行多长时间,T1,T2是不知道的,但是我们可以控制自选次数,如果在一定次数内T0还没有释放锁,意味着竞争相对比较激烈,那么锁就会升级。
假设线程T0拿到了锁,线程T1,T2不会阻塞,而是去自旋,那么T0执行多长时间,T1,T2是不知道的,但是我们可以控制自选次数,如果在一定次数内T0还没有释放锁,意味着竞争相对比较激烈,那么锁就会升级。
偏向锁
轻量级锁
重量级锁
锁消除
锁消除的依据是逃逸分析的数据支持
AbstractQueuedSynchronizer
AQS具备特性
阻塞等待队列
同步等待队列、CLH队列
条件等待队列
共享/独占
Exclusive-独占,只有一个线程能执行,如ReentrantLock
Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
公平/非公平
锁的获取顺序应该符合请求的绝对时间顺序,也就是FIFO。
只要CAS设置同步状态成功,则表示当前线程获取了锁
可重入
允许中断
例子
ReentrantLock
lock.lock() //加锁
lock.unlock() //解锁
ReentrantLock加锁原理
0 条评论
下一页