10.Java锁之自旋锁

概念

自旋锁(spinlock):是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。

  • 之前学过的CAS,底层使用的就是自旋锁,自旋就是多次尝试,多次访问,不会阻塞的状态就是自旋。

优缺点

优点:循环比较获取直到成功,没有类似wait的阻塞。还能减少线程上下文切换的消耗

缺点:当不断自旋的线程越来越多的时候,会因为执行while循环不断的消耗CPU资源

手写自旋锁

自旋的本质就是CAS方法和while循环

public class SpinLockDemo {

    // 原子引用线程
    AtomicReference atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread(); // 返回对当前正在执行的线程对象的引用
        
        System.out.println(Thread.currentThread().getName() + "进来了");

/*  首个线程进来,发现atomicReference是null,就变为这个线程。然后取反变为false,跳出循环等待。
    如果发现卫生间里面还有人,就一直循环一直等,直到卫生间里面没人。  */
        while (!atomicReference.compareAndSet(null, thread)) {
            //没跳出就一直循环
        }
        
    }

    public void myUnlock() {
        Thread thread = Thread.currentThread();
        // 我出去要解锁,然后变为null给下一个人用
        atomicReference.compareAndSet(thread, null);

        System.out.println(Thread.currentThread().getName() + "调用myUnlock方法");
    }

测试

public static void main(String[] args) throws InterruptedException {

    SpinLockDemo spinLockDemo = new SpinLockDemo();

    // 我A线程进去占用这把锁,然后霸占5s
    new Thread(() -> {
        spinLockDemo.myLock();
        
        TimeUnit.SECONDS.sleep(5);        
        
        spinLockDemo.myUnlock();
    }, "线程A").start();

    // 主线程main暂停1s,保证A线程先启动
    TimeUnit.SECONDS.sleep(1);

    // 我B线程再进去循环等待这把锁
    new Thread(() -> {
        spinLockDemo.myLock();
        spinLockDemo.myUnlock();
    }, "线程B").start();
    
}

运行结果

线程A进来了                //A进来了,A要占用5s
线程B进来了                //B也进来了,发现卫生间有人,循环等待A出来
线程A调用myUnlock方法       //A出来了
线程B调用myUnlock方法       //B占用完锁,也出来了

你可能感兴趣的