`

自旋锁

    博客分类:
  • Lock
 
阅读更多

原创转载请注明出处:https://agilestyle.iteye.com/blog/2443652

 

CAS

CAS算法是乐观锁的一种实现方式,CAS算法中又涉及到自旋锁。

CAS是英文单词Compare and Swap(比较并交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。

CAS算法涉及到三个操作数

1.需要读写的内存值 V
2.进行比较的值 A
3.拟写入的新值 B

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B,否则不会执行任何操作。一般情况下是一个自旋操作,即不断的重试。


 

自旋锁

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

 

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。

 

Java实现自旋锁

package org.fool.hellojava.lock;

import java.util.concurrent.atomic.AtomicReference;

public class SpinLockTest {
    private static SpinLock lock = new SpinLock();

    public static void main(String[] args) {
        new Thread(() -> {
            lock.lock();

            test();

            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();

            test();

            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();

            test();

            lock.unlock();
        }).start();
    }

    public static void test() {
        System.out.println(Thread.currentThread().getName() + " invoked test...");

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static class SpinLock {
        private AtomicReference<Thread> cas = new AtomicReference<>();

        public void lock() {
            Thread currentThread = Thread.currentThread();

            while (!cas.compareAndSet(null, currentThread)) {
                System.out.println(Thread.currentThread().getName() + " waiting...");
            }
        }

        public void unlock() {
            Thread currentThread = Thread.currentThread();
            cas.compareAndSet(currentThread, null);
        }

    }
}

lock()方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。

 

自旋锁的缺点

1.如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
2.上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

自旋锁的优点

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

Java实现可重入自旋锁

为了实现可重入锁,需要引入一个计数器,用来记录获取锁的线程数。

package org.fool.hellojava.lock;

import java.util.concurrent.atomic.AtomicReference;

public class ReentrantSpinLockTest {

    private static ReentrantSpinLock lock = new ReentrantSpinLock();

    public static void main(String[] args) {
        new Thread(() -> {
            lock.lock();

            test();

            lock.unlock();
        }).start();
    }

    public static void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + " invoked test...");

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        lock.unlock();
    }

    private static class ReentrantSpinLock {
        private AtomicReference<Thread> cas = new AtomicReference<>();
        private int count;

        public void lock() {
            Thread currentThread = Thread.currentThread();

            if (currentThread == cas.get()) {
                count++;
                return;
            }

            while (!cas.compareAndSet(null, currentThread)) {
                // DO nothing
                System.out.println(Thread.currentThread().getName() + " waiting...");
            }
        }

        public void unlock() {
            Thread currentThread = Thread.currentThread();
            if (currentThread == cas.get()) {
                if (count > 0) {
                    count--;
                } else {
                    cas.compareAndSet(currentThread, null);
                }
            }
        }
    }
}

 

自旋锁与互斥锁

1.自旋锁与互斥锁都是为了实现保护资源共享的机制。
2.无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
3.获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。

自旋锁总结

1.自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。
2.自旋锁等待期间,线程的状态不会改变,线程一直是用户态并且是活动的(active)。
3.自旋锁如果持有锁的时间太长,则会导致其它等待获取锁的线程耗尽CPU。
4.自旋锁本身无法保证公平性,同时也无法保证可重入性。
5.基于自旋锁,可以实现具备公平性和可重入性质的锁。

 

 

 

  • 大小: 24.5 KB
分享到:
评论

相关推荐

    信号量、互斥体和自旋锁的区别

    ### 信号量、互斥体和自旋锁的区别详解 #### 一、基本概念与应用场景 **信号量**、**互斥体**和**自旋锁**是操作系统中三种常用的同步机制,主要用于解决多线程或多进程环境中资源的并发访问问题。这三种机制虽然...

    无锁编程之自旋锁的C++实现

    本文将围绕"无锁编程之自旋锁的C++实现"这一主题,详细介绍无锁编程和自旋锁的概念,并分析提供的C++代码实现。 首先,无锁编程(Lock-Free Programming)强调的是在没有锁的情况下实现并发操作。这种方法的关键...

    基于SMP的Linux内核自旋锁分析.pdf

    "基于SMP的Linux内核自旋锁分析" 本文将对基于SMP(Symmetrical Multi-Processing)的Linux内核自旋锁进行分析。自旋锁是一种常用的并发机制,用于解决多处理器系统中的并发执行问题。在Linux内核中,自旋锁是实现...

    Windows驱动编程视频教程 提升IRQ与自旋锁

    在Windows驱动程序开发中,理解和掌握中断请求级别(IRQ)以及自旋锁是至关重要的。本视频教程将深入探讨这两个核心概念,旨在帮助开发者提升驱动程序的性能和稳定性。 首先,我们来了解一下中断请求级别(IRQ)。...

    自旋锁操作 spin_lock

    自旋锁(Spin Lock)是操作系统中用于多线程同步的一种机制,特别是在处理并发和实时系统中非常常见。自旋锁的基本思想是当一个线程试图获取已被其他线程持有的锁时,它不会立即阻塞,而是不断地循环检查锁的状态,...

    信号量与自旋锁

    ### 信号量与自旋锁:并发控制的关键技术 在多任务操作系统中,尤其是在像Linux这样的高度并发系统中,管理共享资源的访问是一项至关重要的任务。为了确保数据的一致性和完整性,开发人员需要采取措施来避免竞态...

    SQL server 自旋锁争用专题

    SQL Server自旋锁争用是一个高级数据库管理问题,通常出现在高性能、高并发的系统中。自旋锁是操作系统中的一个同步机制,用于控制对共享资源的访问。在数据库系统中,自旋锁用于保护数据结构在并发访问时的完整性。...

    Linux系统内核的同步机制-自旋锁

    Linux系统内核的同步机制是确保多线程和多处理器环境下正确访问共享资源的关键部分,其中自旋锁作为一项重要工具,被广泛用于保护短暂的、临界区的访问。自旋锁的设计理念在于,当一个线程尝试获取已被其他线程持有...

    一种Linux内核自旋锁死锁检测机制的设计与实现.pdf

    Linux 内核自旋锁死锁检测机制设计与实现 在 Linux 内核中,自旋锁是一种广泛应用的锁机制,可以大幅提高系统性能和吞吐量。但是,自旋锁的使用不当,会立即将系统锁死,直至人工重启才能解锁,这是危害性最大的...

    linux系统中基于自旋锁的进程调度的实现

    本文将深入探讨Linux系统中基于自旋锁的进程调度实现,以及自旋锁、共享内存和汇编语言在其中的作用。 自旋锁(Spinlock)是一种简单的互斥机制。当一个进程持有一个自旋锁时,其他试图获取该锁的进程将进入“自旋...

    Java并发篇乐观锁,悲观锁,自旋锁

    本文主要讨论了四种锁类型:乐观锁、悲观锁、自旋锁以及Java中的synchronized同步锁,并深入解析了synchronized锁的内部机制,包括其核心组件、实现方式以及锁的状态。 1. **乐观锁**:乐观锁假设在多线程环境下,...

    深入讲解我们说的CAS自旋锁到底是什么

    深入讲解CAS自旋锁的实现机理和原理 CAS(Compare and Swap)是实现自旋锁或乐观锁的核心操作,它的出现是为了解决原子操作的问题。在多线程环境下,原子操作是保证线程安全的重要手段。CAS操作的实现很简单,就是...

    自旋锁公平性的三种实现代码下载

    自旋锁是多线程编程中的一个重要概念,用于在共享资源上实现轻量级的同步。自旋锁的原理是当一个线程试图获取已被其他线程持有的锁时,它不会立即阻塞,而是会“自旋”等待,直到锁被释放为止。这样做的好处是可以...

    并发控制之自旋锁.pdf

    自旋锁是操作系统中一种重要的同步机制,尤其在多处理器环境下,用于保护共享资源免受并发访问的影响。自旋锁的设计理念是,在等待锁释放的过程中,持有锁的任务会不断地检查锁的状态,即“自旋”,直到锁变为可用...

    linux下自旋锁程序源码.zip

    在Linux操作系统中,自旋锁(Spinlock)是一种用于多线程环境下的低级同步机制。自旋锁的原理是,当一个线程试图获取一个已经被其他线程持有的锁时,它不会像普通互斥锁那样进入睡眠状态,而是会不断地循环检查锁的...

    golang实现Redis分布式自旋锁+本地自旋锁

    在Golang中,实现分布式自旋锁和本地自旋锁是一种常见的并发控制策略,用于解决多线程或分布式系统中的资源竞争问题。自旋锁的基本思想是,当一个线程试图获取锁时,如果锁已被其他线程持有,它会不断地检查锁的状态...

    zynq的linux驱动6-使用自旋锁实现竞争保护

    在这个场景下,“zynq的linux驱动6-使用自旋锁实现竞争保护”是一个关于如何在Zynq SoC的Linux驱动中使用自旋锁来避免数据竞争的重要教程。 自旋锁(Spinlock)是一种同步原语,用于在多线程环境下保护共享资源。在...

    ZYNQ 7010-7020实现自旋锁测试(Linux驱动).zip

    在本文中,我们将深入探讨如何在ZYNQ 7010和7020 FPGA平台上实现自旋锁测试,特别是在Linux驱动程序上下文中。ZYNQ 7010和7020是Xilinx公司推出的SoC(System-on-Chip)系列,集成了ARM Cortex-A9双核处理器,广泛...

    CPU自旋锁优化20191020-CLK-new1

    在现代多核、多CPU插槽的系统中,CPU自旋锁优化对于提升系统性能至关重要。自旋锁是一种同步机制,用于保护临界区,即一段必须被单个线程独占执行的代码。在多处理器环境中,自旋锁可能会引发一系列问题,特别是在非...

Global site tag (gtag.js) - Google Analytics