JUC-自旋锁

自旋锁:spinLock,指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的开销,确点是循环会消耗CPU资源。

public class SpinLockDemo {
    private AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock() {
        System.out.println(Thread.currentThread().getName() + " come in");
        while (!atomicReference.compareAndSet(null, Thread.currentThread())) {}
        System.out.println(Thread.currentThread().getName() + " 持有锁成功");
    }
    public void myUnLock() {
        atomicReference.compareAndSet(Thread.currentThread(), null);
        System.out.println(Thread.currentThread().getName() + " 释放锁成功");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); };
            spinLockDemo.myUnLock();
        }, "t1").start();
        new Thread(()->{
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        }, "t2").start();
    }
}

CAS的缺点:

1、如果CAS失败,会一致进行尝试。如果长时间不成功,CPU空转带来很大开销。(长时间抢不到锁 -- 锁饥饿)

2、会导致ABA问题

CAS算法实现的一个重要前提是需要取出内存中某时刻的数据,并在当下时刻比较并替换。那么在这个时间差中会导致数据的变化。比如说一个线程a,从内存位置V中取出数据x,这时候另一个线程b也从内存取出数据x,然后线程b进行了一些操作,将内存位置V的值变成了y,然后线程b又将内存位置V的数据由y变成x。这时候线程a进行CAS操作,发现内存中数据仍然是x,然后线程a操作成功。尽管线程a的CAS操作成功,但是不代表这个过程就是没有问题的。

如何解决ABA问题?

使用带版本号时间戳原子引用:AtomicStampedReference

ABA示例

public class ABADemo {
    private static AtomicInteger a = new AtomicInteger(100);

    public static void main(String[] args) {
        new Thread(()->{
            a.compareAndSet(100,101);
            a.compareAndSet(101,100);
        }).start();
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); };
        a.compareAndSet(100,200);
    }
}

result = true, 值 = 200 

AtomicStampedReference解决ABA实例

public class ABADemo {
    private static AtomicStampedReference<Integer> a = new AtomicStampedReference<>(100, 1); // 初始值,版本号

    public static void main(String[] args) {
        new Thread(()->{
            int stamp = a.getStamp();
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); };
            a.compareAndSet(100,101, stamp ,stamp + 1);
            a.compareAndSet(101,100, a.getStamp() ,a.getStamp() + 1);
        }).start();

        int stamp = a.getStamp();
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); };
        boolean result = a.compareAndSet(100, 200, stamp, stamp + 1);
        System.out.println("result = " + result + " , 值 = " + a.getReference() + " , 版本号 = " + a.getStamp());
    }
}

result = false , 值 = 100 , 版本号 = 3

猜你喜欢

转载自blog.csdn.net/qq_39940205/article/details/120678908