什么是自旋锁 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,倚天版 1GB 1个月
简介: 什么是自旋锁自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

什么是自旋锁

自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

/**

* 为什么用自旋锁:多个线程对同一个变量一直使用CAS操作,那么会有大量修改操作,

* 从而产生大量缓存一致性流量,因为每一次CAS操作都会发出广播通知其他处理器,从而影响程序的性能。

*

* 自旋不是阻塞,阻塞被唤醒的代价高,性能较差。自旋是执行空代码,虽然效率高,但是会一直占用CPU,

* 最好是很短时间内获得。

*

* 自旋锁:从等待到解锁,线程一直处于running状态,没有上下文切换。

* 注意死递归、死循环导致死锁。

*/


自旋锁+redis


1、导入redis锁



com.baomidou

lock4j-redisson-spring-boot-starter

2.2.2


2、业务实现

自旋锁+redis


1、导入redis锁



com.baomidou

lock4j-redisson-spring-boot-starter

2.2.2


2、业务实现

@Slf4j

@RunWith(SpringRunner.class)

@Slf4j

@RunWith(SpringRunner.class)

@SpringBootTest(classes = TaskApplication.class)

public class ExceptionTest {

@Resource
private LockTemplate lockTemplate;
private final String key = "work_redis_default_lock_test";
@Test
public void test() {
    LockInfo lockInfo = null;
    try {
        //请求超时时间小于等于0L,框架会用默认的请求超时时间,第四个参数决定lock方法用框架里面的自旋锁。设置超时时间。请求时间和过期时间是不一样的概念。
        lockInfo = lockTemplate.lock(key, 50000,
                0L, RedissonLockExecutor.class);
        if (null == lockInfo) {
            log.warn("未抢到锁,结束, key:{}", key);
            return;
        }
        //模拟业务
        long startTime = System.currentTimeMillis();
        Thread.sleep(5000);
        long endTime = System.currentTimeMillis();
        log.info("startTime:{}, endTime:{}, user time:{}", startTime, endTime, endTime - startTime);
    } catch (Exception e) {
        log.error("e:", e);
    } finally {
        //释放锁
        lockTemplate.releaseLock(lockInfo);
    }
}

}


3、结果

2022-07-26 16:47:28.920 INFO 23188 — [ main] ExceptionTest : startTime:1658825243919, endTime:1658825248920, user time:5001


4、dubug

在dubug到这里的时候,实际上,redis已经加了锁。


key是我们定义的key,里面的key类型是hash,key随机产生,value 1。

上锁,其他人无法进来,在外面等待。

设置超时、过期时间,如果中途中断了,这个key也会过期,这样不会影响到其他线程访问,是防止死锁的重要手段。

最后执行释放锁,那redis key会被销毁。


5、源码研究

public LockInfo lock(String key, long expire, long acquireTimeout, Class<? extends LockExecutor> executor) {

acquireTimeout = acquireTimeout < 0L ? this.properties.getAcquireTimeout() : acquireTimeout;

long retryInterval = this.properties.getRetryInterval();

LockExecutor lockExecutor = this.obtainExecutor(executor);

log.debug(String.format(“use lock class: %s”, lockExecutor.getClass()));

expire = !lockExecutor.renewal() && expire <= 0L ? this.properties.getExpire() : expire;

int acquireCount = 0;

String value = LockUtil.simpleUUID();

long start = System.currentTimeMillis();

    try {
        do {
            ++acquireCount;
            Object lockInstance = lockExecutor.acquire(key, value, expire, acquireTimeout);
            if (null != lockInstance) {
                return new LockInfo(key, value, expire, acquireTimeout, acquireCount, lockInstance, lockExecutor);
            }
            TimeUnit.MILLISECONDS.sleep(retryInterval);
        } while(System.currentTimeMillis() - start < acquireTimeout);
        return null;
    } catch (InterruptedException var15) {
        log.error("lock error", var15);
        throw new LockException();
    }
}

实际上这里在执行自旋锁,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

do {

++acquireCount;

Object lockInstance = lockExecutor.acquire(key, value, expire, acquireTimeout);

if (null != lockInstance) {

return new LockInfo(key, value, expire, acquireTimeout, acquireCount, lockInstance, lockExecutor);

}

            TimeUnit.MILLISECONDS.sleep(retryInterval);
        } while(System.currentTimeMillis() - start < acquireTimeout);

自旋锁里面getlock trylock获取锁

public RLock acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
try {
RLock lockInstance = this.redissonClient.getLock(lockKey);
boolean locked = lockInstance.tryLock(acquireTimeout, expire, TimeUnit.MILLISECONDS);
return (RLock)this.obtainLockInstance(locked, lockInstance);
} catch (InterruptedException var9) {
return null;
}
}




相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
16天前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
36 0
|
5天前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
26天前
|
算法 Java 编译器
多线程线程安全问题之系统层面的锁优化有哪些常见的策略
多线程线程安全问题之系统层面的锁优化有哪些常见的策略
|
26天前
|
安全 云计算
云计算自旋锁问题之在线程安全地删除链表节点时,需要频繁加锁会影响性能如何解决
云计算自旋锁问题之在线程安全地删除链表节点时,需要频繁加锁会影响性能如何解决
29 2
|
26天前
|
Java
多线程线程安全问题之什么是锁的粒度,减少锁的粒度有哪些好处
多线程线程安全问题之什么是锁的粒度,减少锁的粒度有哪些好处
|
26天前
多线程线程安全问题之synchronized和ReentrantLock在锁的释放上有何不同
多线程线程安全问题之synchronized和ReentrantLock在锁的释放上有何不同
|
1月前
|
安全 算法 Java
Java 中的并发控制:锁与线程安全
在 Java 的并发编程领域,理解并正确使用锁机制是实现线程安全的关键。本文深入探讨了 Java 中各种锁的概念、用途以及它们如何帮助开发者管理并发状态。从内置的同步关键字到显式的 Lock 接口,再到原子变量和并发集合,本文旨在为读者提供一个全面的锁和线程安全的知识框架。通过具体示例和最佳实践,我们展示了如何在多线程环境中保持数据的一致性和完整性,同时避免常见的并发问题,如死锁和竞态条件。无论你是 Java 并发编程的新手还是有经验的开发者,这篇文章都将帮助你更好地理解和应用 Java 的并发控制机制。
|
20天前
|
消息中间件 算法 Java
(十四)深入并发之线程、进程、纤程、协程、管程与死锁、活锁、锁饥饿详解
本文深入探讨了并发编程的关键概念和技术挑战。首先介绍了进程、线程、纤程、协程、管程等概念,强调了这些概念是如何随多核时代的到来而演变的,以满足高性能计算的需求。随后,文章详细解释了死锁、活锁与锁饥饿等问题,通过生动的例子帮助理解这些现象,并提供了预防和解决这些问题的方法。最后,通过一个具体的死锁示例代码展示了如何在实践中遇到并发问题,并提供了几种常用的工具和技术来诊断和解决这些问题。本文旨在为并发编程的实践者提供一个全面的理解框架,帮助他们在开发过程中更好地处理并发问题。
|
1月前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
26 0
|
1月前
|
存储 安全 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
36 0

相关实验场景

更多