实例讲解Java 自旋锁
作者:java小新人 发布时间:2021-09-17 09:38:50
标签:Java,自旋锁
一直以来不是怎么清楚自旋锁,最近有点时间,好好的学习了一下;
所谓的自旋锁在我的理解就是多个线程在尝试获取锁的时候,其中一个线程获取锁之后,其他的线程都处在一直尝试获取锁的状态,不会阻塞!!!那么什么叫做一直尝试获取锁呢?就是一个循环,比较经典的是AtomicInteger中的一个updateAndGet方法,下图所示(当然也可以直接看unsafe类中的getAndAddInt等类似方法);
我们可以看出在while循环中使用CAS去尝试更新一个变量,如果更新失败,就会一直在这个循环中一直在尝试;成功的话,就可以到最后的return语句;
由此我们可以大概知道如果自旋的线程过多,那么CPU的资源就会被大量消耗!!!
顺便提一个东西叫做原子引用,官方提供了AtomicInteger,AtomicBoolean等原子类,那么如果我们自己定义的类也需要有原子性怎么办呢?所以官方提供了一个AtomicReference类,可以将我们自己定义的类封装一下,就成了我们自己的原子类,例如AtomicReference<Student> atomicReference = new AtomicReference<>();
,然后我们对Student的实例进行CAS各种CAS操作;
栗子:
package TestMain;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
public class TestMain80 {
//一个Thread类的原子引用
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁的方法
public void myLock() {
Thread currentThread = Thread.currentThread();
log.info("myLock--Thread:{}", currentThread.getName());
//这个就是自旋锁的核心,利用CAS比较当前原子引用中是否为null,如果是null,就把当前线程A放到里面去,
// 此时线程B再到这里,那么就会CAS失败,一直在while循环中
while (!atomicReference.compareAndSet(null, currentThread)) {
}
}
//解锁的方法
public void myUnlock() {
Thread currentThread = Thread.currentThread();
//CAS比较原子引用中是不是线程A,是的话就更新为null,此时在上面while中一直在自旋的线程B就可以跳出来了
atomicReference.compareAndSet(currentThread, null);
log.info("myUnlock--Thread:{}", currentThread.getName());
}
public static void main(String[] args) {
TestMain80 testMain80 = new TestMain80();
//线程A,首先加锁,然后等3秒中,然后释放锁
new Thread(() -> {
testMain80.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
testMain80.myUnlock();
}, "A").start();
//主线程等1秒,保证A线程先执行
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程B,加锁再释放锁
new Thread(() -> {
testMain80.myLock();
testMain80.myUnlock();
}, "B").start();
}
}
上面的就是一个自旋锁的栗子,执行结果中首先是执行A线程的myLock方法,获取锁成功,之后的B线程虽然也会执行mylock方法,但是会在while循环中一直阻塞,直到线程A调用了myUnlock方法释放锁,最后两行才会打印出来;
来源:https://www.cnblogs.com/wyq1995/p/12539853.html
0
投稿
猜你喜欢
- 一、概念 1. 为了能让程序操作数据库,对数据库中的表进行操作,每一种数据库都会提供一套连接和操作该数据库的驱动,而且每种数据库
- //计算字符串的MD5值 public string G
- 编写RedisConfig首先我们要明白RedisConfig中需要包含什么,首先看看我们直接使用RedisTemplate的问题,我们就知
- 实体类时间格式化java 实体类 时间格式化注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:s
- 1.SQL注入:程序向后台数据库传递SQL时,用户提交的数据直接拼接到SQL语句中并执行,从而导入SQL注入攻击。字符型注入:黑色部分为拼接
- 概述从今天开始, 小白我将带大家开启 Jave 数据结构 & 算法的新篇章.链表链表 (Linked List) 是一种递归的动态数
- Java float和double精度范围大小要想理解float和double的取值范围和计算精度,必须先了解小数是如何在计算机中存储的:举
- 简介说明本文用实例来介绍@Autowired解决循环依赖的原理。@Autowired是通过 * 缓存来解决循环依赖的。 除了@Aut
- 前言在一个名为种花家的小镇上,生活着一群热爱编程的人。他们致力于构建出高效、可维护的软件系统,而 Spring Boot 框架成为了他们的不
- 拆分字符串:这个可以使用两次分割,第一次使用 | 分割,放到arr数组里,然后使用循环对arr[i]进行使用:分割public static
- 一、输入输出流对象cout:标准输出流cerr:标准出凑 和cout(只是用于如果是错误时要输出的)cin :&nb
- java 中模式匹配算法-KMP算法实例详解朴素模式匹配算法的最大问题就是太低效了。于是三位前辈发表了一种KMP算法,其中三个字母分别是这三
- Java中线程的创建有两种方式: 1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在
- JDK1.7以及以前:接口(interface)在JDK7及之前的版本对接口的要求:接口定义:使用 interface 关键字 。接口中的
- 无论是用Eclipse还是用Android Studio做android开发,都会接触到jar包,全称应该是:Java Archive,即j
- Java中提供了大数字(超过16位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecim
- 使用ExecutorService来停止线程服务之前的文章中我们提到了ExecutorService可以使用shutdown和shutdow
- 1、说明isInterrupted()可以判断当前线程是否被中断,仅仅是对interrupt()标识的一个判断,并不会影响标识发生任何改变(
- 前言:项目中我们经常会遇到有时候需要等待其他线程完成任务后,主线程才能执行其他任务,那么我们将如何实现呢?Join 解决方案join 的工作
- 本文实例讲述了Java接口继承和使用接口操作。分享给大家供大家参考,具体如下:一 接口的继承1 点睛接口支持多继承,一个接口可以有多个父接口