加锁保护共享资源
无锁保护共享资源
用的是CAS的思想。
AtomicInteger不用加锁就能保护共享变量的线程安全。
关键是compareAndSet(CAS)配合while(true)来使用
上图是CAS工作的流程。
CAS代码分析:
比较value和prev的值,如果一样就输出next,如果不一致,就把prev改成value的值然后接着循环。value是别的线程修改后的balance的值。
CAS的效率更高,比synchronized高。原因是无锁情况下就算重试失败,线程仍然在高速运行。但synchronized会让线程在没有锁的情况下发生阻塞,进入上下文切换。
CAS在多核cpu能发挥优势,如果没有cpu,线程就分不到时间片仍然会进入可运行状态导致上下文切换。
特点
CAS是乐观锁的思想:具体为不阻止别的线程使用共享资源,改了的话重试即可。
synchronized是悲观锁的思想:具体为给共享资源上锁,不给别的线程用。
CAS线程不会阻塞,但如果竞争激烈就会频繁发生重试,效率降低。
原子整数(AtomicInteger)
一些方法
getandAdd等于上面的实现。
上面是做原子性乘法运算。
自己写原子性乘法。
原子引用
和原子整数使用方法类似
用原子引用来实现逻辑 BigDecimal类型使用原子引用。
AtomicReference
这种情况下主线程无法感知到其他线程对共享变量的修改,因为他查的只是共享变量的最新值。
AtomicStampReference
通过获取版本号可以改变这种情况。加一个stamp版本号,需要看版本号便没变化。
追踪原子引用整个的变化过程。
AtomicMarkableReference
不关心改了几次,只关心改没改过。
原子数组
保护的是数组里的元素
创建原子数组。
原子更新器
保护对象赋值的一个原子性:
原子累加器
supplier:提供累加器对象,有返回结果。()->结果
consumer:有参数但是没有返回结果。执行累加操作。
比较了两种原子累加器:AtomicLong和LongAdder,专门做累加的。
LongAdder性能更高。
性能提升是因为在有竞争的时候设置了多个累加单元,在多个共享变量 Cell()上进行累加,重试次数减少,最后再把结果汇总。
LongAdder的原理
用CAS实现锁
0表示加锁,1表示没加锁。
测试代码如下:
Cell共享单元实现逻辑:
缓存行伪共享
通过上面的@sun.misc.Contended来实现
cell是数组形式,一个cell是24字节(16字节的对象头和8字节的value)
原理是使用该注释的对象或字段的前后各增加128字节大小的空白,让cpu把对象读到缓存的时候占用不同的缓存行
这样当cpu core1要修改cell0,而cpu core2要修改cell1的时候,就不会造成对方的缓存行失效。
一个缓存行加了多个cell对象叫伪共享。
cpu和内存的运行效率差距大,所以需要预读数据到缓存来提升效率。
缓存由缓存行组成,每个缓存行对应一段内存大约是64byte
如果某个cpu核更改了数据,那么该数据在其他cpu核对应的缓存行失效。
LongAdder类里add方法的实现(源码):
LongAccumulate方法实现(源码)
sum方法的实现(源码)
unsafe对象
原子整数,原子引用,原子数组,原子更新器,原子累加器,底层都是unsafe,基于unsafe类来实现的。
获取unsafe实例对象。
给teacher赋值
上面是unsafe对象cas基本应用