跳至主要內容

自旋锁

zheng大约 3 分钟java基础

自旋锁

1、自旋锁概念(spinlock)

是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。

2、自旋锁的优点 :

自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

3、自旋锁应用 :

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。

如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

4、简单自旋锁的实现 :

public class SimpleSpinLock {
     /**
      * 持有锁的线程,null表示锁未被线程持有
      */
     private static AtomicReference<Thread> ref = new AtomicReference<>();

public void Lock() {
         Thread currentThread = Thread.currentThread();
         // 当ref为null的时候compareAndSet返回true,反之为false
         // 通过循环不断的自旋判断锁是否被其他线程持有
         while (!ref.compareAndSet(null, currentThread)) {
         }
     }

   public void unLock() {
        Thread currentThread = Thread.currentThread();
         if (ref.get() != currentThread) {
         }
         ref.set(null);
     }
 }

test:

public class SimpleSpinLockTest {

    private static int n = 0;

    public static void main(String[] args) throws InterruptedException {
         ThreadPoolExecutor pool = new ThreadPoolExecutor(100, 100, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new DefaultNameThreadFactory("SimpleSpinLock"));
         CountDownLatch countDownLatch = new CountDownLatch(100);
         SimpleSpinLock simpleSpinLock = new SimpleSpinLock();
         for (int i = 0; i < 100; i++) {
             pool.submit(() -> {
                 simpleSpinLock.Lock();
                 n++;
                 simpleSpinLock.unLock();
                 // 计数减一
                 countDownLatch.countDown();
             });
         }
         // 要求主线程等待所有任务全部准备好才一起并行执行
         countDownLatch.await();
         System.out.println(n);
     }
 }

5、可重入的自旋锁和不可重入的自旋锁 :

仔细分析一下上述就可以看出,它是不支持重入的,即当一个线程第一次已经获取到了该锁,在锁释放之前又一次重新获取该锁,第二次就不能成功获取到。

由于不满足CAS,所以第二次获取会进入while循环等待,而如果是可重入锁,第二次也是应该能够成功获取到的。为了实现可重入锁,我们需要引入一个计数器,用来记录获取锁的线程数----》其他章节可重入锁

6、 另有三种常见的形式 :

TicketLock ,CLHlock 和 MCSlock:https://www.cnblogs.com/stevenczp/p/7136416.htmlopen in new window

上次编辑于:
贡献者: 郑天祺