CPU|java面试——常见的锁和锁升级( 二 )


自旋锁的优缺点优点:避免了锁的使用 , 总体性能比synchronized高很多
缺点:存在锁竞争的情况下会造成cpu资源的浪费 , 因为自旋锁一直在让cpu空转直到锁升级到重量级锁
优点
  1. 尽可能的减少线程的阻塞 , 对于锁竞争不激烈 , 并且占用锁时间较短的代码来说 , 适合用自旋锁 , 因为他的开销相比重量级锁比如synchronized的线程wait和唤醒来说 , 消耗比较小 , 重量级锁在睡眠和唤醒需要切换两次上下文 。
    因为cas同时只能有一个线程获取到锁 , 其他都会失败 , 失败了就自旋 , 自旋是不会释放cpu资源的 , 这样就会有大量线程占用cpu资源 , 进而会影响整体系统的性能 , 所以自旋周期的选择就比较重要了 , Jdk1.5这个限度是一定的写死的 , 在1.6引入了适应性自旋锁
    自适应自旋锁
    • 由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定 。
    • 如果有半数cpu数个线程正在自旋 , 则后来线程直接阻塞
缺点
  1. 容易造成cpu资源浪费
用的是汇编指令:lock 是保证原子性的 ,
也就防止在写回去比较的过程中别的线程再次把值修改掉!
lock cmpxchg=cas修改的变量值

再往硬件层面走就是lock锁定了北桥芯片的一个电信号 , 这就没啥意义了
CAS里面的ABA问题如何解决?什么是ABA问题
比如 , 第一个线程把0改为1准备往回写的时候 , 去读取原来的值 , 这个值已经被另外一个线程读取过去先改为2然后再改为0放回去 , 那么此时第一个线程去判断的0其实已经不是原来的那个0了
如何解决ABA问题?很简单 , 在原来那个0上加一个版本号 , 任何值的改动都需要更新版本号 , 当比较的时候除了比较值 , 还要比较版本号 , jdk1.5之后 , 引入AtomicStampedReference类来解决ABA问题 。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用 , 并且检查当前标志是否等于预期标志 , 如果二者都相等 , 才使用CAS设置为新的值和标志 。
如何手写一个自旋锁?
new AtomicReference<>()
class MySpinLock{
AtomicReference<Thread> reference = new AtomicReference<>();
public void lock(){
System.out.println(Thread.currentThread().getName()+\"  want to get lock\");
//reference空说明还没有线程获取到过
while (!reference.compareAndSet(nullThread.currentThread())){
System.out.println(Thread.currentThread().getName()+\"  try to get Lock\");

System.out.println(Thread.currentThread().getName()+\"  got the lock\");

public void unLock(){
System.out.println(Thread.currentThread().getName()+\"  want to  Unlock\");
//解锁的话判断reference是否是当前线程 , 是的话就置为空
reference.compareAndSet(Thread.currentThread()null);
System.out.println(Thread.currentThread().getName()+\"  Unlock \");


public static void main(String[
 args) {
MySpinLock mySpinLock = new MySpinLock();
new Thread(()->{
mySpinLock.lock();
try {
TimeUnit.SECONDS.sleep(1);
catch (InterruptedException e) {
e.printStackTrace();

mySpinLock.unLock();
\"A\").start();

new Thread(()->{