gpt4 book ai didi

java - 如何在没有同步块(synchronized block)(即低成本锁)的情况下在一个安全操作中原子地检查 Java 中的两个 AtomicBooleans?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:58:35 24 4
gpt4 key购买 nike

所以我有两个 AtomicBoolean,我需要检查它们。类似的东西:

if (atomicBoolean1.get() == true && atomicBoolean2.get() == false) {

// ...
}

但两者之间存在竞争条件 :(

有没有办法将两个原子 boolean 检查组合成一个而不使用同步(即同步块(synchronized block))?

最佳答案

好吧,我可以想到几种方法,但这取决于您需要的功能。

一种方法是“作弊”并使用 AtomicMarkableReference<Boolean> :

final AtomicMarkableReference<Boolean> twoBooleans = (
new AtomicMarkableReference<Boolean>(true, false)
);

void somewhere() {
boolean b0;
boolean[] b1 = new boolean[1];
b0 = twoBooleans.get(b1);

b0 = false;
b1[0] = true;

twoBooleans.set(b0, b1);
}

但这有点痛苦,只会给你两个值。

那么您可以将 AtomicInteger 与 bit flags 一起使用:

static final int FLAG0 = 1;
static final int FLAG1 = 1 << 1;

final AtomicInteger intFlags = new AtomicInteger(FLAG0);

void somewhere() {
int flags = intFlags.get();

int both = FLAG0 | FLAG1;
if((flags & both) == FLAG0) { // if FLAG0 has a 1 and FLAG1 has a 0
something();
}

flags &= ~FLAG0; // set FLAG0 to 0 (false)
flags |= FLAG1; // set FLAG1 to 1 (true)

intFlags.set(flags);
}

虽然有点痛苦,但它会给你 32 个值。如果你真的想要的话,你可以围绕它创建一个包装类。例如:

public class AtomicBooleanArray {
private final AtomicInteger intFlags = new AtomicInteger();

public void get(boolean[] arr) {
int flags = intFlags.get();

int f = 1;
for(int i = 0; i < 32; i++) {
arr[i] = (flags & f) != 0;
f <<= 1;
}
}

public void set(boolean[] arr) {
int flags = 0;

int f = 1;
for(int i = 0; i < 32; i++) {
if(arr[i]) {
flags |= f;
}
f <<= 1;
}

intFlags.set(flags);
}

public boolean get(int index) {
return (intFlags.get() & (1 << index)) != 0;
}

public void set(int index, boolean b) {
int f = 1 << index;

int current, updated;
do {
current = intFlags.get();
updated = b ? (current | f) : (current & ~f);
} while(!intFlags.compareAndSet(current, updated));
}
}

这还不错。也许在 get 中复制数组时执行了一组,但重点是您可以获取以原子方式设置所有 32 个。 (比较和设置 do-while 循环非常丑陋,但它是原子类本身如何处理 getAndAdd 之类的东西。)

AtomicReference 在这里似乎不切实际。它允许原子获取和设置,但一旦你掌握了内部对象,你就不再以原子方式更新。您每次都必须创建一个全新的对象。

final AtomicReference<boolean[]> booleanRefs = (
new AtomicReference<boolean[]>(new boolean[] { true, true })
);

void somewhere() {
boolean[] refs = booleanRefs.get();

refs[0] = false; // not atomic!!

boolean[] copy = booleanRefs.get().clone(); // pretty safe
copy[0] = false;
booleanRefs.set(copy);
}

如果您想以原子方式对数据执行临时操作(获取 -> 更改 -> 设置,无干扰),您必须使用锁或同步。就我个人而言,我会使用锁或同步,因为通常情况下,整个更新就是您想要保留的内容。

** 不安全!! **

不要这样做!

这可以(可能)用 sun.misc.Unsafe 来完成。这是一个使用 Unsafe 写入易变长牛仔风格的两半的类。

public class UnsafeBooleanPair {
private static final Unsafe UNSAFE;

private static final long[] OFFS = new long[2];
private static final long[] MASKS = new long[] {
-1L >>> 32L, -1L << 32L
};

static {
try {
UNSAFE = getTheUnsafe();

Field pair = UnsafeBooleanPair.class.getDeclaredField("pair");
OFFS[0] = UNSAFE.objectFieldOffset(pair);
OFFS[1] = OFFS[0] + 4L;

} catch(Exception e) {
throw new RuntimeException(e);
}
}

private volatile long pair;

public void set(int ind, boolean val) {
UNSAFE.putIntVolatile(this, OFFS[ind], val ? 1 : 0);
}

public boolean get(int ind) {
return (pair & MASKS[ind]) != 0L;
}

public boolean[] get(boolean[] vals) {
long p = pair;
vals[0] = (p & MASKS[0]) != 0L;
vals[1] = (p & MASKS[1]) != 0L;
return vals;
}

private static Unsafe getTheUnsafe()
throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe)theUnsafe.get(null);
}
}

重要的是,Javadoc in the Open JDK source对于 fieldOffset 表示 对偏移量进行算术运算。然而,用它做算术似乎确实有效,因为我没有得到垃圾。

这对整个单词进行了一次 volatile 读取,但也(可能)对其中任何一半进行了 volatile 写入。 putByteVolatile 可用于将 long 拆分为 8 个段。

我不建议任何人使用它(不要使用它!),但它作为一个奇怪的东西有点有趣。

关于java - 如何在没有同步块(synchronized block)(即低成本锁)的情况下在一个安全操作中原子地检查 Java 中的两个 AtomicBooleans?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21925546/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com