CAS - 自定义自旋锁

        前面一篇文章我们学习了CAS的基本原理。CAS是实现自旋锁的基础,CAS利用CPU指令保证的操作的原子性,已达到锁的效果。至于自旋,就是字面意思“自己旋转”,是指尝试获取共享资源的线程不会立即阻塞,而是采用循环的方法尝试去获取锁,当线程发现共享资源被占用,就会不断的判断锁的状态,知道获取。这样的好处就是减少线程上下文切换的消耗,缺点是消耗CPU资源。

自己实现一个自旋锁(SpinLock)

        前面一篇文章我们学习的AtomicInteger的基本实现,我们是用和其类似的AtomicReference来实现我们自定义的自旋锁。AtomicReference和AtomicInteger差不多,都是使用CAS来实现的,只不过AtomicReference承载的是一个任意对象,而AtomicInteger是一个Integer。

/**
 * @author tlh
 * @date 2023/8/7 21:17
 */
public class SpinLockDemo {

    private AtomicReference atomicReference = new AtomicReference<>();

    /**
     * 上锁
     */
    public void lock() {
        Thread currentThread = Thread.currentThread();
        while (!atomicReference.compareAndSet(null, currentThread)) {
            System.out.println(currentThread.getName() + " 正在尝试获取");
        }
        System.out.println(currentThread.getName() + " 获得了资源");
    }
    /**
     * 释放锁
     */
    public void unLock() {
        Thread currentThread = Thread.currentThread();
        atomicReference.compareAndSet(currentThread, null);
        System.out.println(currentThread.getName() + " 任务完成,释放资源");
    }
 }

        我们自定义的自旋锁SpinLockDemo,有一个AtomicReference的属性和有两个方法lock()方法和unLock()方法。

lock()方法

        lock()方法里面有一个while循环,循环里面适应AtomicReference的compareAndSet()方法实现自旋:第一个线程进来,先判断AtomicReference实例里面是否没有存在线程对象,如果不存在的话就将当前线程保存起来(获得锁的控制权)。如果存在,就不停的重试(自旋)。

unLock()方法

        unLock()方法,将当前AtomicReference实例中的线程重置为空(让出锁的控制权)。

使用自定义的SpinLock

 public static void main(String[] args) {

        SpinLockDemo spinLock = new SpinLockDemo();

        new Thread(() -> {
            spinLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 做自己的任务");
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLock.unLock();
        }, "A").start();


        //保证A线程比B线程先获得锁
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLock.lock();
            System.out.println(Thread.currentThread().getName() + " 做自己的任务");
            spinLock.unLock();
        }, "B").start();
    }

        A线程和B线程抢占我们自定义的自旋锁,为了让A线程先获得锁的控制权,我们在A线程start后暂停了100毫秒。

        打印结果:

A 获得了资源
A 做自己的任务
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取

......

B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
B 正在尝试获取
A 任务完成,释放资源
B 获得了资源
B 做自己的任务
B 任务完成,释放资源

        我们看到结果,A线程先获得锁资源,B线程在A线程没有释放锁资源之前一直处于“尝试获取资源”的状态。

你可能感兴趣的:(Java并发编程,SpinLock,自旋锁)