自旋锁原理及基于原子引用手写自旋锁

什么是自旋锁

        自旋锁(Spinlock)是一种用于多线程同步的机制,在尝试获取锁时,如果锁已经被其他线程持有,则当前线程不会立即被阻塞,而是会进入一个循环中反复尝试获取锁,直到成功为止。这种机制通过“自旋”等待锁释放,从而避免了线程的上下文切换,但在某些情况下也可能导致CPU资源的浪费。

工作原理

  1. 尝试获取锁:当一个线程需要访问共享资源时,它会尝试获取自旋锁。如果锁已经被其他线程持有,则当前线程不会立即进入阻塞状态。

  2. 自旋等待:当前线程会进入一个循环(通常称为“自旋”),在这个循环中它会不断检查锁是否已经被释放。这通常是通过一个原子操作(如CAS,Compare-And-Swap)来实现的,该操作会检查锁的状态并尝试将其设置为已锁定。

  3. 获取锁成功:如果锁已经被释放(即其他线程已经完成了对共享资源的访问并释放了锁),则当前线程会成功获取锁,并退出自旋循环。

  4. 执行临界区代码:一旦获取了锁,当前线程就可以安全地访问共享资源,并执行临界区代码。

  5. 释放锁:完成临界区代码的执行后,当前线程会释放锁,以便其他线程可以获取锁并访问共享资源。

优缺点

优点

  • 避免上下文切换:由于自旋锁不会使线程进入阻塞状态,因此可以避免线程上下文切换的开销。这对于锁持有时间较短且上下文切换开销较大的场景来说是有益的。

  • 减少线程调度开销:由于自旋锁不会使线程进入阻塞状态,因此线程调度器不需要为这些线程分配和回收资源,从而减少了线程调度的开销。

缺点

  • CPU资源浪费:如果锁持有时间较长,自旋锁会导致CPU资源的浪费。因为线程在不断自旋等待锁释放的过程中会占用CPU资源,而这些资源本可以用于其他有用的工作。

  • 可能导致优先级反转:在优先级反转的场景中,低优先级的线程可能持有锁,而高优先级的线程在等待锁。如果高优先级的线程不断自旋等待锁释放,它可能会消耗大量的CPU资源,而低优先级的线程却得不到足够的CPU时间来释放锁。

适用场景

自旋锁适用于以下场景:

  • 锁持有时间较短:如果锁的持有时间非常短(例如,几微秒到几十微秒),则自旋锁可以避免线程上下文切换的开销,并提高性能。

  • 多核处理器环境:在多核处理器环境中,自旋锁可以更好地利用CPU资源。因为即使一个线程在等待锁释放时占用了CPU资源,其他核心仍然可以执行其他有用的工作。

  • 避免线程切换开销较大的场景:在某些情况下,线程切换的开销可能非常大(例如,在实时系统中)。在这些情况下,使用自旋锁可以避免线程切换的开销,并提高系统的响应性。

自实现一个自旋锁

源码

package com.test;

import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName : SpinLockTest
 * @Description: 自旋锁Case
 * @Author: liulianglin
 * @Date: 2024-10-27 9:30
 * @Version : 1.0
 */
public class SpinLockTest {

    AtomicReference<Thread> reference = new AtomicReference<>(null);

    public void lock(){
        System.out.println(Thread.currentThread().getName() + "\t  coming in  and try lock....");
        while (!reference.compareAndSet(null, Thread.currentThread())){

        }
        System.out.println(Thread.currentThread().getName() + "\t get lock succ....");
    }


    public void unlock(){
        reference.compareAndSet(Thread.currentThread(), null);
        System.out.println(Thread.currentThread().getName() + "\t unlock and coming out....");
    }

    public static void main(String[] args) throws InterruptedException {
        SpinLockTest spinLockTest = new SpinLockTest();
        Thread t1 = new Thread(()->{
            spinLockTest.lock();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockTest.unlock();
        }, "t1");
        t1.start();


        // 等待t1启动完毕
        Thread.sleep(1000);

        Thread t2 = new Thread(()->{
            spinLockTest.lock();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockTest.unlock();
        }, "t2");
        t2.start();


    }

}

运行效果

        t1线程首先启动运行并率先占有锁,然后持有锁5秒钟。t2线程启动运行后,先尝试获取锁,一直获取不到则会不停的进行while循环,直到获取到锁为止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值