自旋锁与阻塞锁

简介: 自旋锁与阻塞锁

@[toc]

自旋锁

阻塞或者唤醒一个Java线程需要操作系统切换CPU 状态来完成,这种状态转换 需要耗费处理器时间

如果同步代码块到代码过于简单,状态转换到时间有kennel比用户执行代码到时间还长

在许多场景下,同步资源到锁定时间短,为了这小段时间切换线程,线程的挂起和恢复可能会让系统得不偿失,

这里是为了当前线程“ 稍等一下”, 我们需要让当前线程进行自旋 ,如果自旋完成后前面锁定同步资源的线程以及释放了锁,那么当前线程就没必要阻塞,而是直接获取同步资源,从而避免线程的开销


阻塞锁和自旋锁相反,阻塞锁如果没有拿到锁,就会直接阻塞,知道被唤醒

阻塞锁

阻塞锁是指当线程尝试获取锁失败时,线程进入阻塞状态,直到接收信号后被唤醒.(线程的状态包括新建、就绪、运行、阻塞及死亡)在JAVA中,能够唤醒阻塞线程的操作包括Object.notify, Object.notifyAll, Condition.signal, LockSupport.unpark(JUC中引入)

原理和源码分析

在 jdk 1.5 及以上并发框架 Java.util.concurrent 的 atomic 下 都是自旋锁实现的

AtomicInteger 的实现 :自旋锁的世勋啊原理是CAS
AtomicInteger 中是调用底层unsafe 进行自增操作的源码中的 do-while 循环就是一个自旋操作,如果修改过程中一踏线程竞争导致修改失败,就在while 死循环,直至成功

package com.dimple.test;

/**
 * 本实例演示下线程的自旋锁的实现
 */
public class SpinLockDemo {
    private static AtomicReference<Thread> atomicReference=new AtomicReference<>();
    public void lock(){
        Thread thread=Thread.currentThread();
        while(!atomicReference.compareAndSet(null,thread)){
            System.out.println(thread.getName()+"自旋锁获取失败,重新获取中");
        }
    }
    public void unlock(){
        Thread thread=Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
    }
 
    public static void main(String[] args) {
        SpinLockDemo spinLockDemo=new SpinLockDemo();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"尝试获取自旋锁");
            spinLockDemo.lock();
            System.out.println(Thread.currentThread().getName()+"获取自旋锁成功");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.unlock();
            System.out.println(Thread.currentThread().getName()+"释放自旋锁");
        }).start();
 
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"尝试获取自旋锁");
            spinLockDemo.lock();
            System.out.println(Thread.currentThread().getName()+"获取自旋锁成功");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.unlock();
            System.out.println(Thread.currentThread().getName()+"释放自旋锁");
        }).start();
    }
}


Thread-0尝试获取自旋锁
Thread-0获取自旋锁成功
Thread-1尝试获取自旋锁
Thread-1自旋锁获取失败,重新获取中
Thread-1自旋锁获取失败,重新获取中
Thread-1自旋锁获取失败,重新获取中
Thread-1自旋锁获取失败,重新获取中

我们打开 AtomicInteger 源码看一下,这里是个 do while 循环
在这里插入图片描述

自旋锁的应用场景

自旋锁一般用于多核服务器,在并发度不是特别搞的情况下,比阻塞锁效率高,
另外,自旋锁适用于临界区较小的情况,否则如果临界区很大,(线程一旦拿到锁,很多之后才会释放),那也是不适合的

相关文章
|
4月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
59 0
|
6月前
|
Java
JUC(11)各种锁的理解(公平锁、可重入锁、自旋锁、死锁)
这篇文章介绍了Java并发包中的各种锁机制,包括公平锁与非公平锁、可重入锁、自旋锁以及死锁的概念、实现和示例,以及如何使用jps和jstack工具来检测和诊断死锁问题。
|
6月前
|
SQL 关系型数据库 MySQL
MySQL使用行级锁时,并非直接锁定记录,而是锁定涉及的索引。对于表`user_item`,更新语句先锁定非主键索引`idx_1`,再锁定主键索引。若两条更新语句分别按不同顺序锁定这两个索引,可能导致互相等待对方释放锁,引发死锁。解决方案包括先查询待更新记录的主键,再按主键更新,确保一致的锁定顺序。
66 2
|
8月前
|
调度
线程操作:锁、条件变量的使用
线程操作:锁、条件变量的使用
46 1
|
8月前
|
Java 调度
阻塞锁和自旋锁的理解
总体来说,自旋锁适用于锁定时间短、锁竞争不频繁的场景,而阻塞锁更适合锁定时间较长或锁竞争较频繁的场景。根据具体的应用需求选择合适的锁类型,可以优化系统性能。
119 0
|
9月前
|
Linux
Linux线程同步(try锁和读写锁)
Linux线程同步(try锁和读写锁)
103 0
|
Linux API C++
锁、避免死锁等相关
锁、避免死锁等相关
82 0
|
存储
5. 多线程并发锁
5. 多线程并发锁
58 0
|
存储 算法 安全
辛辛苦苦的劳动成果,如何上把锁?
辛辛苦苦的劳动成果,如何上把锁?
|
生物认证
什么是锁?
什么是锁?
167 0
什么是锁?

热门文章

最新文章