[英]Share local variable value between barrier threads in java
我一直致力于实现一个自定义循环屏障,它添加传递给 await 方法的值,并在调用通知后将总和返回给所有线程。
编码:
public class Barrier {
private final int parties;
private int partiesArrived = 0;
private volatile int sum = 0;
private volatile int oldSum = 0;
public Barrier(int parties) {
if (parties < 1) throw new IllegalArgumentException("Number of parties has to be 1 or higher.");
this.parties = parties;
}
public int getParties() { return parties; }
public synchronized int waitBarrier(int value) throws InterruptedException {
partiesArrived += 1;
sum += value;
if (partiesArrived != parties) {
wait();
}
else {
oldSum = sum;
sum = 0;
partiesArrived = 0;
notifyAll();
}
return oldSum;
}
public int getNumberWaiting() { return partiesArrived; }
}
这有效,但我听说有一种方法可以将sum
和oldSum
(或至少oldSum
)的值更改为waitBarrier
方法的局部变量。 然而,在我绞尽脑汁之后,我看不出有什么办法。
是否可能,如果是,如何?
您可以返回sum
并让第一方清除它:
public synchronized int waitBarrier(int value) throws InterruptedException {
if (partiesArrived == 0) {
sum = 0;
}
partiesArrived++;
sum += value;
if (partiesArrived == parties) {
notifyAll();
} else {
while (partiesArrived < parties) {
wait();
}
}
return sum;
}
请注意,在虚假唤醒的情况下,应始终在循环中检查等待条件。 此外,如果不在synchronized
块之外访问sum
,则它不需要是volatile
的。
然而,在我绞尽脑汁之后,我看不出有什么办法。
这么。
是否可能,如果是,如何?
这不可能。
一些证明:
尝试将本地 var 标记为volatile
。 它不起作用:编译器不允许。 为什么不呢? 因为 volatile必然是一个空操作:本地变量根本不能与另一个线程共享。
有人可能认为这是“共享”本地人:
void test() {
int aLocalVar = 10;
Thread t = new Thread(() -> {
System.out.println("Wow, we're sharing it! " + aLocalVar);
});
t.start();
}
但它是一些语法糖,让你在那里绊倒:实际上(你可以用javap -c -v
确认这一点,以显示javac
为这个代码制作的字节码),本地 var 的副本被传递到这里的块。 这就解释了为什么在 Java 中,除非您尝试共享的变量是 [A] 标记为final
或 [B] 可以无错误地标记(这称为“变量实际上是最终的”),否则上面的编译失败')。 如果 java 允许您像这样访问非(有效)决赛,并且 java 使用可用的复制机制,那将非常令人困惑。
当然,在java中,所有非原语都是引用。 指针,用其他一些语言的说法。 因此,您可以“共享”(不是真的,它将是一个副本)一个本地变量,但仍然可以获得您想要的(在 2 个线程之间共享状态),因为当您获得变量的副本时,该变量只是一个指针。 就像这样:如果我有一张纸,它是我的,但我可以把它扔进复印机,也给你一份,我们似乎不能共享状态。 我在纸上刮的东西不会神奇地出现在你的纸上; 这不是巫毒纸。 但是,如果我的纸上有一个房子的地址,我复制它并递给你一份副本,感觉就像我们在分享:如果你走到房子,我不知道,从窗户扔一块砖,然后我走过去,我可以看到它。
Java 中的许多对象是不可变的(不受砖块影响),并且原语不是引用。 一种解决方案是使用AtomicX
系列,它们只是原始或引用的简单包装器,使它们可变:
AtomicInteger v = new AtomicInteger();
Thread t = new Thread(() -> {v.set(10);});
t.start();
t.yield();
System.out.println(t.get());
// prints 10
但是这里没有发生本地的实际共享。 该线程获得了对位于堆上的单个 AtomicInteger 实例的引用的副本,并且两个线程最终都“走到了房子”,在这里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.