友情提示:
本文原创&首发于公众号:程序员古德
原文标题:Java并发基础:原子类之AtomicBoolean全面解析
本文概要
AtomicBoolean
类优点在于能够确保布尔值在多线程环境下的原子性操作,避免了繁琐的同步措施,它提供了高效的非阻塞算法实现,可以大大提成程序的并发性能,AtomicBoolean
的API设计非常简单易用。
AtomicBoolean核心概念
AtomicBoolean
是java.util.concurrent.atomic
中的一个类,它提供了一个原子性的布尔值,这个布尔值的读取和设置是线程安全的,不会发生线程间的冲突。
模拟一个业务场景来说明AtomicBoolean
的作用,假设,有一个电商平台系统,其中一个功能是管理促销活动的开启和关闭状态,促销活动可能由多个线程或服务同时访问和修改其状态,比如一个线程可能负责定时检查促销活动的结束时间并在时间到达时关闭活动,而另一个线程可能负责实时接收运营人员的操作指令来手动开启或关闭活动。
为了确保这两个线程能够正确地更新和读取促销活动的开启状态,而不发生数据不一致的情况,可以使用 AtomicBoolean
控制这个状态:
- 当运营人员通过后台界面开启活动时,负责接收操作指令的线程会将
AtomicBoolean
设置为true
。 - 当促销活动自然结束或运营人员手动关闭活动时,相应的线程会将
AtomicBoolean
设置为false
。 - 同时,其他任何需要读取活动状态的线程,如前端展示促销活动的页面,都可以安全地读取这个
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
来控制服务的状态,startService
和stopService
方法分别用于开启和关闭服务,它们通过调用AtomicBoolean
的set
方法来设置状态,isServiceRunning
方法返回当前服务的运行状态,它通过调用AtomicBoolean
的get
方法来获取状态。
ClientThread
类是一个线程类,它模拟客户端请求服务的行为,在run
方法中,它调用服务的clientRequest
方法,该方法根据服务的运行状态来处理请求。
AtomicBoolean核心API
AtomicBoolean
类是Java的java.util.concurrent.atomic
包中的一个原子类,用于对布尔值进行原子操作,以下是AtomicBoolean
类中主要方法的含义:
AtomicBoolean(boolean initialValue)
- 构造函数,用于创建一个具有给定初始值的
AtomicBoolean
实例。
- 构造函数,用于创建一个具有给定初始值的
boolean get()
- 获取当前值,此方法以原子方式读取
AtomicBoolean
实例的当前值,并返回它。
- 获取当前值,此方法以原子方式读取
void set(boolean newValue)
- 设置新值,此方法以原子方式设置
AtomicBoolean
实例的值。
- 设置新值,此方法以原子方式设置
boolean compareAndSet(boolean expect, boolean update)
- 如果当前值与预期值
expect
相等,则以原子方式将该值设置为update
,并返回true
;否则返回false
,这是一个条件原子更新操作,常用于实现无锁算法或数据结构。
- 如果当前值与预期值
boolean getAndSet(boolean newValue)
- 以原子方式设置
AtomicBoolean
的值为newValue
,并返回旧值,这个方法可以用于实现一些需要知道旧值的同时更新为新值的场景。
- 以原子方式设置
boolean weakCompareAndSet(boolean expect, boolean update)
- 与
compareAndSet
方法类似,但允许更大的并发性,可能会失败更多(即返回false
),即使在当前值与预期值相同的情况下也是如此,这个方法通常用于循环中,直到成功为止。不过,由于它可能“失败更多”,因此它通常比compareAndSet
更快。
- 与
String toString()
- 返回
AtomicBoolean
实例的当前值的字符串表示形式("true"
或"false"
)。
- 返回
int hashCode()
- 返回该
AtomicBoolean
实例的哈希码值。
- 返回该
boolean equals(Object obj)
- 检查此
AtomicBoolean
实例与另一个对象是否相等。如果对象也是一个AtomicBoolean
实例,并且两个实例的当前值相同,则返回true
;否则返回false
。
- 检查此
AtomicBoolean技术原理
AtomicBoolean
是 java.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;
}
// 默认构造函数,默认值为false
public 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); }
}
}
从上述代码可以看出:
value
变量使用了volatile
关键字,保证了多线程环境下的内存可见性和禁止指令重排序。- 提供了如
compareAndSet()
、getAndSet()
等原子操作方法,这些方法底层通过Unsafe
类提供的CAS
操作实现,能在硬件层面保证操作的原子性,即在同一时间只有一个线程能修改value值,不会出现竞态条件。 - Boolean值被转换为int值进行存储和操作,这是因为JVM无法直接对boolean类型进行CAS操作,而对int类型可以。
友情提示:
本文原创&首发于公众号:程序员古德
公众号原文地址:https://mp.weixin.qq.com/s/ckXlC7V4xC4nU0T0ipvxeA
自我总结
AtomicBoolean
类的优点在于原子性操作,可确保在多线程环境中对布尔值的读取和设置不会产生竞态条件,同时,它的性能通常优于使用synchronized
的代码,因为它避免了线程阻塞和上下文切换的开销。同时,AtomicBoolean
还提供了丰富的API,如compareAndSet
和getAndSet
等。但是,虽然AtomicBoolean
提供了原子性保证,但它却无法解决并发中的可见性和有序性问题,这里需要特别注意。
END!
END!
END!
往期回顾
精品文章
Java并发基础:concurrent Flow API全面解析
Java并发基础:CopyOnWriteArraySet全面解析
Java并发基础:ConcurrentSkipListMap全面解析