Java并发基础:原子类之AtomicBoolean全面解析

Java并发基础:原子类之AtomicBoolean全面解析 - 程序员古德

本文概要

AtomicBoolean类优点在于能够确保布尔值在多线程环境下的原子性操作,避免了繁琐的同步措施,它提供了高效的非阻塞算法实现,可以大大提成程序的并发性能,AtomicBoolean的API设计非常简单易用。

AtomicBoolean核心概念

AtomicBooleanjava.util.concurrent.atomic中的一个类,它提供了一个原子性的布尔值,这个布尔值的读取和设置是线程安全的,不会发生线程间的冲突。

模拟一个业务场景来说明AtomicBoolean的作用,假设,有一个电商平台系统,其中一个功能是管理促销活动的开启和关闭状态,促销活动可能由多个线程或服务同时访问和修改其状态,比如一个线程可能负责定时检查促销活动的结束时间并在时间到达时关闭活动,而另一个线程可能负责实时接收运营人员的操作指令来手动开启或关闭活动。

为了确保这两个线程能够正确地更新和读取促销活动的开启状态,而不发生数据不一致的情况,可以使用 AtomicBoolean 控制这个状态:

  1. 当运营人员通过后台界面开启活动时,负责接收操作指令的线程会将 AtomicBoolean 设置为 true
  2. 当促销活动自然结束或运营人员手动关闭活动时,相应的线程会将 AtomicBoolean 设置为 false
  3. 同时,其他任何需要读取活动状态的线程,如前端展示促销活动的页面,都可以安全地读取这个 AtomicBoolean 的值,而不用担心读到的是一个正在被其他线程修改的中间状态。

AtomicBoolean类主要用来解决并发编程中的线程安全问题,特别是在需要对一个共享布尔变量进行原子性读取和修改的场景中,它的内部使用了硬件级别的原子操作来保证对布尔值的读取和设置是线程安全的,因此,在多线程环境中,当一个线程正在修改AtomicBoolean的值时,其他线程无法同时修改它,必须等待当前线程的操作完成后才能继续。

AtomicBoolean使用案例

下面是一个简单的Java代码示例,演示了如何使用AtomicBoolean类,这实例将创建一个模拟的服务,使用一个AtomicBoolean来控制其状态(开启或关闭),并通过多个线程来模拟客户端的调用,如下代码案例:

import java.util.concurrent.atomic.AtomicBoolean;  // Service类使用AtomicBoolean来控制其状态  
public class AtomicService {  private AtomicBoolean isRunning = new AtomicBoolean(false);  // 开启服务  public void startService() {  isRunning.set(true);  System.out.println("Service started.");  }  // 关闭服务  public void stopService() {  isRunning.set(false);  System.out.println("Service stopped.");  }  // 检查服务是否正在运行  public boolean isServiceRunning() {  return isRunning.get();  }  // 客户端调用服务的模拟方法  public void clientRequest() {  if (isServiceRunning()) {  System.out.println("Client request processed successfully as the service is running.");  } else {  System.out.println("Client request failed as the service is not running.");  }  }  
}  // 客户端线程,模拟多个客户端同时请求服务  
class ClientThread extends Thread {  private AtomicService service;  public ClientThread(AtomicService service) {  this.service = service;  }  @Override  public void run() {  service.clientRequest();  }  
}  // 主类,包含main方法,用于启动示例  
public class AtomicBooleanDemo {  public static void main(String[] args) throws InterruptedException {  AtomicService atomicService = new AtomicService();  // 开启服务  atomicService.startService();  // 创建并启动多个客户端线程  Thread client1 = new ClientThread(atomicService);  Thread client2 = new ClientThread(atomicService);  client1.start();  client2.start();  client1.join(); // 等待线程执行完成  client2.join(); // 等待线程执行完成  // 关闭服务  atomicService.stopService();  // 再次尝试通过客户端线程请求服务,应该会失败  Thread client3 = new ClientThread(atomicService);  client3.start();  client3.join(); // 等待线程执行完成  }  
}

上面代码中,AtomicService类使用一个AtomicBoolean成员变量isRunning来控制服务的状态,startServicestopService方法分别用于开启和关闭服务,它们通过调用AtomicBooleanset方法来设置状态,isServiceRunning方法返回当前服务的运行状态,它通过调用AtomicBooleanget方法来获取状态。

ClientThread类是一个线程类,它模拟客户端请求服务的行为,在run方法中,它调用服务的clientRequest方法,该方法根据服务的运行状态来处理请求。

AtomicBoolean核心API

AtomicBoolean类是Java的java.util.concurrent.atomic包中的一个原子类,用于对布尔值进行原子操作,以下是AtomicBoolean类中主要方法的含义:

  1. AtomicBoolean(boolean initialValue)
    • 构造函数,用于创建一个具有给定初始值的AtomicBoolean实例。
  2. boolean get()
    • 获取当前值,此方法以原子方式读取AtomicBoolean实例的当前值,并返回它。
  3. void set(boolean newValue)
    • 设置新值,此方法以原子方式设置AtomicBoolean实例的值。
  4. boolean compareAndSet(boolean expect, boolean update)
    • 如果当前值与预期值expect相等,则以原子方式将该值设置为update,并返回true;否则返回false,这是一个条件原子更新操作,常用于实现无锁算法或数据结构。
  5. boolean getAndSet(boolean newValue)
    • 以原子方式设置AtomicBoolean的值为newValue,并返回旧值,这个方法可以用于实现一些需要知道旧值的同时更新为新值的场景。
  6. boolean weakCompareAndSet(boolean expect, boolean update)
    • compareAndSet方法类似,但允许更大的并发性,可能会失败更多(即返回false),即使在当前值与预期值相同的情况下也是如此,这个方法通常用于循环中,直到成功为止。不过,由于它可能“失败更多”,因此它通常比compareAndSet更快。
  7. String toString()
    • 返回AtomicBoolean实例的当前值的字符串表示形式("true""false")。
  8. int hashCode()
    • 返回该AtomicBoolean实例的哈希码值。
  9. boolean equals(Object obj)
    • 检查此AtomicBoolean实例与另一个对象是否相等。如果对象也是一个AtomicBoolean实例,并且两个实例的当前值相同,则返回true;否则返回false

AtomicBoolean技术原理

AtomicBooleanjava.util.concurrent.atomic 中的一个类,它提供了线程安全的方式来操作布尔值,它可以确保多个线程对同一个布尔值的操作是原子的,并且对这个布尔值的操作任何时候都只能由一个线程执行。

实现原理

AtomicBoolean的实现基于硬件级别的原子操作,它使用Java的Unsafe类来直接访问内存,并执行底层的原子操作。

Unsafe类提供了一些可以直接操作内存的低级方法,包括原子性的比较和交换(compare-and-swap,CAS)操作,CAS是一种无锁算法,它包含三个操作数——内存位置(V)、预期原值(A)和新值(B),CAS会检查内存位置V的值是否与预期原值A相等,如果是,则将该位置的值设置为新值B,这个过程是原子的,也就是说,在这个操作进行期间,不会有其他线程能够改变内存位置V的值。

AtomicBoolean的内部实现中,布尔值被存储在一个volatile修饰的字段中,以确保所有线程都能看到最新的值,volatile关键字保证了内存可见性和禁止指令重排序。

底层算法

AtomicBoolean 类中的主要方法是 get(), set(), compareAndSet(), getAndSet(), lazySet(), 和 weakCompareAndSet(),这些核心方法都使用了底层的 CAS 操作来实现原子性,如下:

  • get() 方法,直接返回当前存储的布尔值。
  • set(boolean newValue) 方法,使用 Unsafe 类的 putOrderedObject() 方法来设置新的布尔值,虽然这个方法名看起来可能不是原子的,但实际上对于布尔值这种单个字段的写入,它是原子的,不过,set() 操作本身并不保证其他线程的立即可见性,但在后续的读取操作中,由于 volatile 关键字的存在,会保证读取到的是最新的值。
  • compareAndSet(boolean expect, boolean update) 方法,这是一个典型的 CAS 操作,它首先检查当前值是否与期望的值相等,如果是,则更新为新值,这个过程是原子的。
  • getAndSet(boolean newValue) 方法,这个方法会设置新的值,并返回旧的值,它内部也是通过 CAS 操作来实现的。

虽然 AtomicBoolean 的实现基于 CAS,但它并不是锁或者同步原语,它使用了一种称为无锁编程的技术,通过避免使用传统的锁机制来减少线程间的竞争和阻塞,从而提高并发性能。

小总结AtomicBoolean 是通过底层硬件支持的原子操作和 Java 内存模型中的 volatile 关键字来实现线程安全的布尔值操作的,通过它,可以用来实现各种无锁的数据结构和算法。

核心代码实现

public class AtomicBoolean implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// 使用volatile修饰符确保可见性和有序性private volatile int value;// 将value转换为布尔值private static final boolean BOOLEAN_TRUE = true;private static final int INT_TRUE = 1;// 构造函数public AtomicBoolean(boolean initialValue) {value = initialValue ? 1 : 0;}// 默认构造函数,默认值为falsepublic AtomicBoolean() {this(false);}// 原子性的设置值public final boolean getAndSet(boolean newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue ? 1 : 0);}// 原子性的比较并交换public final boolean compareAndSet(boolean expect, boolean update) {return unsafe.compareAndSwapInt(this, valueOffset, expect ? 1 : 0, update ? 1 : 0);}// 获取当前值public final boolean get() {return value != 0;}// Unsafe类的操作private static final sun.misc.Unsafe unsafe =sun.misc.Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicBoolean.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}
}

从上述代码可以看出:

  1. value变量使用了volatile关键字,保证了多线程环境下的内存可见性和禁止指令重排序。
  2. 提供了如compareAndSet()getAndSet()等原子操作方法,这些方法底层通过Unsafe类提供的CAS操作实现,能在硬件层面保证操作的原子性,即在同一时间只有一个线程能修改value值,不会出现竞态条件
  3. Boolean值被转换为int值进行存储和操作,这是因为JVM无法直接对boolean类型进行CAS操作,而对int类型可以。

自我总结

Java并发基础:原子类之AtomicBoolean全面解析 - 程序员古德

AtomicBoolean类的优点在于原子性操作,可确保在多线程环境中对布尔值的读取和设置不会产生竞态条件,同时,它的性能通常优于使用synchronized的代码,因为它避免了线程阻塞和上下文切换的开销。同时,AtomicBoolean还提供了丰富的API,如compareAndSetgetAndSet等。但是,虽然AtomicBoolean提供了原子性保证但它却无法解决并发中的可见性和有序性问题,这里需要特别注意。

关注我,每天学习互联网编程技术 - 程序员古德

END!
END!
END!

往期回顾

精品文章

Java并发基础:concurrent Flow API全面解析

Java并发基础:CopyOnWriteArraySet全面解析

Java并发基础:ConcurrentSkipListMap全面解析

Java并发基础:ConcurrentSkipListSet全面解析!

Java并发基础:SynchronousQueue全面解析!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/696155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1024 [NOIP2001 提高组] 一元三次方程求解

P1024 [NOIP2001 提高组] 一元三次方程求解 纯代码记录 #include <iostream> #include <math.h> using namespace std; double a,b,c,d; double res[3];//用于存放三个解 int resCount;inline double F(double x)//三次函数 {return a*pow(x,3)b*pow(x,2)c*xd; }//…

web前端开发this指向问题

❗ 函数内部中的 this 指向谁&#xff0c;不是在函数定义时决定的&#xff0c;而是在函数第一次调用并执行的时候决定的 1. call 方法 语法&#xff1a;函数名.call(调用者, 参数1, …) 作用&#xff1a;函数被借用时&#xff0c;会立即执行&#xff0c;并且函数体内的this会…

Facebook Horizon:探索虚拟现实中的社交空间

随着科技的不断进步&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正成为社交互动和娱乐体验的新前沿。在这个数字时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;正在引领虚拟社交的新时代&#xff0c;其推出的虚拟社交平台Facebook Horizon成为了…

Tomcat线程池原理(下篇:工作原理)

文章目录 前言正文一、执行线程的基本流程1.1 JUC中的线程池执行线程1.2 Tomcat 中线程池执行线程 二、被改造的阻塞队列2.1 TaskQueue的 offer(...)2.2 TaskQueue的 force(...) 三、总结 前言 Tomcat 线程池&#xff0c;是依据 JUC 中的线程池 ThreadPoolExecutor 重新自定义…

深入理解C语言(5):程序环境和预处理详解

文章主题&#xff1a;程序环境和预处理详解&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3c4…

Imagewheel私人图床搭建结合内网穿透实现无公网IP远程访问教程

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

flutter 文件上传组件和大文件分片上传

文件分片上传 资料 https://www.cnblogs.com/caijinglong/p/11558389.html 使用分段上传来上传和复制对象 - Amazon Simple Storage Service 因为公司使用的是亚马逊的s3桶 下面是查阅资料获得的 亚马逊s3桶的文件上传分片 分段上分为三个步骤&#xff1a;开始上传、上传对…

CSP-J 2023 T3 一元二次方程

文章目录 题目题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 题目传送门题解思路总代码 提交结果尾声 题目 题目背景 众所周知&#xff0c;对一元二次方程 a x 2 b x c 0 , ( a ≠ 0 ) ax ^ 2 bx c 0, (a \neq 0) ax2bxc0,(a0)&#xff0c;可…

STM32G030C8T6:定时器1ms中断(以64MHz外部晶振为例)

本专栏记录STM32开发各个功能的详细过程&#xff0c;方便自己后续查看&#xff0c;当然也供正在入门STM32单片机的兄弟们参考&#xff1b; 本小节的目标是&#xff0c;系统主频64 MHZ,采用高速外部晶振&#xff0c;通过定时器3 每秒中断控制 PB9 引脚输出高低电平&#xff0c;从…

20240222作业

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示“登录成功"&#xff0c;提供一个Ok按钮&#xff0c;用户点击OK后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&…

Java基础-注解

注解 注解是用来干什么的它有什么作用注解的常见分类内置注解Override注解定义 Deprecated注解定义 SuppressWarnings注解定义 元注解Target注解定义ElementType Retention&&RetentionTarget注解定义RetentionPolicy Documented注解定义 Inherited注解定义用法 Repeata…

低代码开发:推动互联网企业数字化转型的关键因素

联网行业作为我国数字经济发展的核心驱动力&#xff0c;在推动国家数字化转型中扮演着至关重要的角色。与其他传统行业相比&#xff0c;互联网企业面临更加紧迫的数字化转型需求&#xff0c;因为它们需要不断适应快速变化的市场环境和技术趋势。 然而&#xff0c;由于互联网企业…

深入理解APDU协议与Java开发

1. 什么是APDU&#xff1f; APDU&#xff0c;即应用协议数据单元&#xff08;Application Protocol Data Unit&#xff09;&#xff0c;是一种在智能卡与卡片读卡器之间进行通信的协议。APDU定义了在交互中传输的数据格式和规则&#xff0c;允许读卡器发送指令并接收响应。 2…

MFC 皮肤库配置

1.创建MFC 对话框 2.添加皮肤资源 添加资源 添加头文件 关闭SDL检测 添加静态库文件 修改字符集 添加头文件 将皮肤中的ssk文件加载到初始化实例中 > 运行即可

springboot 的 websocket 里面使用 @Autowired 注入 service 或 bean 时,报空指针异常

直接上解决方案&#xff1a; 在你的WebSocketServer服务器中 public static MessageService messageService; //要注入的类// 注入的时候&#xff0c;给类的 service 注入Autowiredpublic void setChatService(MessageService messageService) {WebSocketServer.messageSer…

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一)

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一) 大家好 我是寸铁&#x1f44a; 总结了一篇刷题关于树、dfs、bfs、回溯、递归的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 105. 从前序与中序遍历序列构造二叉树 模拟分析图 代码实现 /*** Definition for a binary tre…

HarmonyOS—添加/删除Module

Module是应用/服务的基本功能单元&#xff0c;包含了源代码、资源文件、第三方库及应用/服务配置文件&#xff0c;每一个Module都可以独立进行编译和运行。一个HarmonyOS应用/服务通常会包含一个或多个Module&#xff0c;因此&#xff0c;可以在工程中创建多个Module&#xff0…

如何利用内网穿透工具在企业微信开发者中心实现本地接口服务回调

文章目录 1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 企业微信开发者在应用的开发测试阶段&#xff0c;应用服务通常是部署在开发环境&#xff0c;在有数据回调的开发场…

SQL查询每个类别价格前3的数据

SELECTproduct_id,category,price FROM (SELECTproduct_id,category,price,ROW_NUMBER() OVER (PARTITION BY category ORDER BY price) AS rankFROMyour_products_table ) AS ranked_products WHERErank < 3;DENSE_RANK() 和 ROW_NUMBER() 是窗口函数&#xff08;Window Fu…

前端知识复习

1.symbol类型 Symbol 是 ECMAScript 6 中引入的一种新的基本数据类型&#xff0c;它表示独一无二的值。Symbol 值是通过 Symbol() 函数创建的。 Symbol 值具有以下特点&#xff1a; 独一无二性&#xff08;唯一性&#xff09;&#xff1a;每个通过 Symbol() 函数创建的 Symb…