自旋锁:获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,一直循环到获取锁为止。
非自旋锁: 获取不到锁,线程就进入阻塞状态。等待 CPU 唤醒,再去获取
想象以下场景:某线程去获取锁(可能是自旋锁 or 非自旋锁),然而锁现在被其他线程占用了。它两获取锁的执行流程就如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nx7IljzW-1650758877574)(https]
😕/img-blog.csdnimg.cn/5d49d27596c04b97ab2c7b25d6e52d27.png)
自选锁的好处与坏处:避免了线程切换的开销,但可能会消耗CPU资源。
适用场景:并发不能太高,避免一直自旋不成功,线程执行的同步任务不能太复杂,耗时比较短 。在手写自旋锁时,先了解自选锁的实现原理,在java.util.concurrent的包中,里面的原子类基本都是自旋锁的实现。我们看看做常用的 AtomicInteger 类,它里面有个 getAndIncrement 方法,然后底下的有个调用Unsafe 的 getAndAddInt 方法 源码如下:
参数:this为创建atomicInteger实例对象、valueOffset为内存偏移量、1为修改值
大致意义:先获取内存储值:var5,通过var1和var2获取期望值与var5进行比较,若为true则var5+var4并返回,否则就一直循环。
UnSafe:是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
CAS :它是一条CPU并发原语,它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的,也就是说在执行过程中不允许被中断保证了当前线程的数据时一致的。
输出结果为: 第一条允许修改,修改值为2019。第二条不允许因为期望值(原生)5被A修改
因此在多线程环境下,原子操作是保证线程安全的重要手段,但只能保证一个线程,并且消耗资源大。
理由在以下案例: 通过CAS(比较并修改)+自旋锁展示:
UnSafe实现+CAS思想(自旋锁)
