共享模型之无锁

1
2
3
4
5
6
7
8
9
+ 问题提出
+ 保护共享资源
+ 加锁实现
+ synchronize、reentrantlock
+ 无锁实现
+ AtomicInteger:原子整数
+ int封装
+ compareAndSet(prev,next)
+ 修改成功为true

CAS与volatile

cas

image-20220315223914515

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+ cas与volatile
+ 工作方式
+ cas原子
+ CAS就是JAVA里唯一的乐观锁了...
+ 保证了CAS原子性
+ 慢动作分析
+ volatile
+ 保证了变量的可见性
+ 保证获取新值比较
+ 效率分析
+ 无锁始终运行,有锁会上下文切换
+ runnable——>blocked
+ 代价比较大
+ 无锁线程保存运行,需要额外cpu支持
+ 没有分到时间片,变成可运行状态,还是会上下文阻塞
+ 多核cpu无锁才有优势
+ 特点
+ 线程少 + 多核cpu
+ 体现乐观锁思想
+ synchronize体现悲观锁思想
+ cas体现无锁开发、无阻塞并发

原子整数

J.U.C 并发包提供了:

  • AtomicBoolean

  • AtomicInteger

  • AtomicLong

以 AtomicInteger 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+ 原子整数
+ AtomicInteger 原子方法
+ compareAndSet
+ 配合while(true)
+ 自增
+ incrementAndGet():++i
+ getAndIncrement:i++
+ decrementAndGet ...
+ 加法
+ getAndAdd()
+ addAndGet()
+ 这里没有将这些方法底层 底层还是循环获取最新值才能进行加法
+ updateAndGet 表达式
+ 参数:函数接口
+ 原理
+ 用do{}while()吧,源码也是用的do{}while()
+ 通用封装
+ 官方实现

原子引用

为什么需要原子引用类型?

  • AtomicReference

  • AtomicMarkableReference

  • AtomicStampedReference

有如下方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+ 原子引用
+ AtomicReference
+ 基本类型的赋值也是原子操作,但是要保证成员变量在线程之间的可见性,并且不使用synchronized这种重量级的互斥锁,就需要到atomic包下面的无锁操作
+ bigdecimal
+ 本来就没啥差,就是内部的value是引用类型了
+ ABA问题
+ 问到CAS必问ABA
+ 无法判断是否被别人修改过,只能判断值相同
+ 其他线程A-B-A ,值仍不变,但是修改过了
+ AtomicStampedReference
+ 比较值 + 版本号
+ 维护版本号
+ AtomicMarkableReference
+ 只关心是否被更改过
+ boolean记录
+ 使用AtomicStampedReference也可以实现相同的效果
+ 这其实还是为了解决ABA问题,就是打了一个标记。

原子数组

1
2
3
4
5
6
7
+ 原子数组
+ 不想改变引用本身,修改里面的元素,如数组
+ 这里需要说一下,compareAndSet()的比较是通过引用地址比较的,之前是对String举例,String的不可变性导致了我们每次对String的更改也导致了引用的更改
+ 测试
+ 线程不安全
+ 改为原子数组AtomicIntegerArray
+ 线程安全

原子更新器

1
2
3
4
+ 原子更新器
+ 针对对象某个字段进行原子操作
+ 必须volatile
+ integer、long、reference等

原子累加器

1
2
3
4
5
6
7
8
9
10
+ 原子累加器
+ 性能比原子整数好
+ 原子整数测试
+ 200w次累加 57
+ 原子累加器测试
+ 200w次累加 24
+ 原理
+ 设置多个累加单元cell(不会超过cpu核心)
+ 最后将结果汇总
+ 操作不同累加单元cell,cas重试次数减少数

LongAdder原理

@sun.misc.Contended

image-20220315224104497

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
+ LongAdder(原子累加器)原理
+ cas锁
+ cellsbusy
+ cas加锁,防止扩容时,多个线程同时扩容
+ 状态标记,用cas来保证原子更改状态
+ 只有更改状态成功——获得锁
+ 就是ReentransLock 使用 AQS 加锁过程
+ 这个就是基本的操作系统实现锁的方式啊。。。
+ 缓存行伪共享
+ cell类
+ contended,防止缓存行伪共享
+ cpu内存结构
+ 缓存以缓存行为单位
+ 缓存加入造成数据副本产生
+ 保证数据一致性
+ 一个核心对缓存数据更改,另一个核心缓存要失效
+ 两个cell可以放进一个缓存行
+ 对cell进行修改,导致其他核心的缓存行失效
+ 解决
+ 让cell存在于不同缓存行
+ @sun.misc.Contended
+ 字段前后各加128字节大小的空隙
+ 保证将对象读到缓存,占用不同缓存行
+ 不会造成其他核心缓存行失效
+ add
+ 源码最重要
+ LongAccumulate
+ cells未创建
+ cell未创建
+ 逻辑性太强了
+ cell已创建
+ 重要的是要能串起来
+ sum
+ 累加单元求和

Unsafe对象

Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ unsafe对象
+ 获取
+ cas、park等都是调用unsafe中方法
+ 反射获取对象
+ cas相关方法
+ cas操作,修改域值
+ 有竞争,需要while重试
+ 模拟实现原子整数
+ AtomicInteger模拟实现
+ 反射获取unsafe对象
+ int成员变量,volatile
+ 获取int偏移量,保护域变量
+ get
+ decrement
+ 循环
+ cas赋值

小结

1
2
3
4
5
6
7
8
9
10
11
12
+ cas、volatile
+ 无锁并发
+ api
+ 原子整数
+ 原子引用
+ 原子数组
+ 字段更新器
+ 原子累加器
+ unsafe
+ 原理
+ LongAdder
+ 伪共享

共享模型之不可变

不可变对象

string

image-20220315224233141

1
2
3
4
5
6
7
8
+ 不可变对象
+ 使用
+ 不可变 日期时间类
+ 设计
+ string类
+ final修饰,不可修改
+ 保护性拷贝
+ 通过创建副本对象,避免共享,线程安全

享元模式

image-20220315224255945

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
+ 享元模式
+ 定义和实现
+ 对相同取值的对象进行共享,减少内存使用
+ 体现
+ 事先创建好256个long对象到缓存数组
+ 不超过范围就重用,从数组中获取,避免对象重复创建
+ byte、short、long、integer(上限可变):-128-127
+ char:0-127、boolean:true false
+ string、bigdecimal
+ 不可变线程安全辨析
+ 就是因为是不可变类才可以用享元模式,不然一个可变的对象你咋能多个线程和变量共享而确定其值你是你需要的?
+ 单个方法线程安全,但组合不一定是安全的,前面就讲过,说牵强的好好听课
+ 自定义连接池
+ 分析
+ 连接池,重用
+ 开发工程师?搬砖码农?
+ 实现
+ 属性、构造
+ final size、数组、数组状态(原子)
+ Integer只是调用单个方法线程安全,修改值之前需要查看状态是否为0然后置为1,这个时候就有可能有线程问题
+ 方法
+ 获取连接
+ 乐观锁,cas获取空闲连接
+ 无空闲连接,加锁,进入wait等待
+ 归还连接
+ 直接修改状态(单线程)
+ 通知其他线程,notifyall
+ 测试
+ 总结
+ 获取不到连接,wait
+ 防止cpu空转获取
+ 非短时间
+ 优化
+ 连接扩缩容
+ 连接可用性检测
+ 超时处理
+ 分布式hash

final原理

1
2
3
4
5
6
7
8
9
+ final原理
+ 设置final变量的原理
+ 赋值final变量之后,加入写屏障
+ 前面指令不会重排序到后面
+ 前面变量操作同步到主存,可见性
+ 获取final变量的原理
+ 不加final共享堆空间,从堆中获取
+ 加了复制到栈内存,或者使用常量池中的,用的是复制的
+ 加快访问速度

无状态

1
2
+ 不可变:有成员,不可以改变
+ 无状态:没有成员变量(状态信息),没有状态,更是线程安全的

小结

1
2
3
4
5
6
7
8
+ 不可变类使用
+ 不可变类设计
+ 保护性拷贝
+ 原理
+ final
+ 模式
+ 享元模式
+ 重用对象,减少对内存的使用