AtomicBoolean概念、使用案例
它提供了线程安全的方式来操作布尔值
,它可以确保多个线程对同一个布尔值的操作是原子的
,并且只能由一个线程执行对这个布尔值的操作
,避免了繁琐的同步措施
,它提供了高效的非阻塞算法实现
,可以大大提成程序的并发性能,AtomicBoolean的API设计非常简单易用。使用案例如下:
BarWorker实现了Runnable接口,该类中有个boolean属性,需求是:有多个BarWorker线程的情况下,只有一个线程执行boolean属性=true的逻辑。下面分别用Boolean、和AtomicBoolean编码,验证只有AtomicBoolean才能实现需求。
首先通过Boolean:
import java.util.concurrent.TimeUnit;
public class BarWorker implements Runnable {
private static boolean exists = false;
private String name;
public BarWorker(String name) {
this.name = name;
}
@Override
public void run() {
if (!exists) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// do nothing
}
exists = true;
System.out.println(name + " enter");
System.out.println(name + " working");
System.out.println(name + " leave");
} else {
System.out.println(name + " give up");
}
}
public static void main(String[] args) {
BarWorker bar1 = new BarWorker("bar1");
BarWorker bar2 = new BarWorker("bar2");
new Thread(bar1).start();
new Thread(bar2).start();
}
}
执行结果:发现并不能实现需求
bar1 enter
bar2 enter
bar1 working
bar2 working
bar1 leave
bar2 leave
通过AtomicBoolean编码如下:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class BarWorker2 implements Runnable {
private static AtomicBoolean exists = new AtomicBoolean(false);
private String name;
public BarWorker2(String name) {
this.name = name;
}
@Override
public void run() {
if (exists.compareAndSet(false, true)) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// do nothing
}
System.out.println(name + " enter");
System.out.println(name + " working");
System.out.println(name + " leave");
} else {
System.out.println(name + " give up");
}
}
public static void main(String[] args) {
BarWorker2 bar1 = new BarWorker2("bar1");
new Thread(bar1).start();
BarWorker2 bar2 = new BarWorker2("bar2");
new Thread(bar2).start();
}
}em.out.println(name + " give up");
}
}
public static void main(String[] args) {
BarWorker2 bar1 = new BarWorker2("bar1");
new Thread(bar1).start();
BarWorker2 bar2 = new BarWorker2("bar2");
new Thread(bar2).start();
}
}
执行结果如下:实现了需求,可见AtomicBoolean的compareAndSet方法:在比较、更改2个操作之间,不能执行其他指令
bar1 enter
bar2 give up
bar1 working
bar1 leave
AtomicBoolean核心api及原理
AtomicBoolean类是Java的java.util.concurrent.atomic包中的一个原子类,用于对布尔值进行原子操作,以下是AtomicBoolean类中主要方法的含义:
-
AtomicBoolean(boolean initialValue)
构造函数,用于创建一个具有给定初始值的AtomicBoolean实例。 -
boolean get()
获取当前值,此方法以原子方式读取AtomicBoolean实例的当前值,并返回它。 -
void set(boolean newValue)
- 设置新值,此方法以原子方式设置AtomicBoolean实例的值。
- set(boolean newValue) 方法,使用 Unsafe 类的 putOrderedObject() 方法来设置新的布尔值,虽然这个方法名看起来可能不是原子的,但实际上对于布尔值这种单个字段的写入,它是原子的,
不过,set() 操作本身并不保证其他线程的立即可见性
,但在后续的读取操作中,由于 volatile 关键字的存在,会保证读取到的是最新的值
。
- boolean compareAndSet(boolean expect, boolean update)
- 如果当前值与预期值expect相等,则以原子方式将该值设置为update,并返回true;否则返回false,这是一个条件
原子更新操作
,常用于实现无锁算法
或数据结构。 - Unsafe类提供了一些可以直接操作内存的低级方法,包括原子性的比较和交换(compare-and-swap,CAS)操作,
CAS是一种无锁算法
,它包含三个操作数——内存位置(V)、预期原值(A)和新值(B),CAS会检查内存位置V的值是否与预期原值A相等,如果是,则将该位置的值设置为新值B,这个过程是原子的,也就是说,在这个操作进行期间,不会有其他线程能够改变内存位置V的值
。 - AtomicBoolean的内部实现中,布尔值被存储在
一个volatile修饰的字段中
,以确保所有线程都能看到最新的值
,volatile关键字保证了内存可见性和禁止指令重排序
。
- boolean getAndSet(boolean newValue)
- 以原子方式设置AtomicBoolean的值为newValue,并返回旧值,这个方法可以用于实现一些需要知道旧值的同时更新为新值的场景,
也能保证原子性
。 - 这个方法会设置新的值,并返回旧的值,它内部也是通过 CAS 操作来实现的。
-
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类的优点在于原子性操作
,可确保在多线程环境中对布尔值的读取和设置不会产生竞态条件
,同时,它的性能通常优于使用synchronized的代码
,因为它避免了线程阻塞和上下文切换的开销
。同时,AtomicBoolean还提供了丰富的API,如compareAndSet和getAndSet等。但是,虽然AtomicBoolean提供了原子性保证,但它却无法解决并发中的可见性和有序性问题
,这里需要特别注意。
其他
volatile 链接
CountDownLatch:链接1、链接2
Thread.join 链接