在java的屏障线程之间共享本地变量值



我一直致力于实现一个自定义的循环屏障它将传入await方法的值相加,并在调用afternotify时将总和返回给所有线程。

代码:

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; }
}

这是有效的,但我听说有一种方法可以将sumoldSum(或至少oldSum)的值更改为waitBarrier方法的局部变量。然而,在绞尽脑汁之后,我找不到任何方法。

是否可能?如果可能,如何实现?

然而,在绞尽脑汁之后,我看不出有什么办法。

如此。

是否可能,如果是,如何?

这是不可能的

对于一些证明:

尝试将本地变量标记为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为该代码生成的字节码),将本地变量的副本传递给这里的块。这就解释了为什么在java中,除非您试图共享的变量是[A]标记为final,或者[B]可以被标记为没有错误(这被称为"变量实际上是最终的"),否则上述代码无法编译。如果java允许您访问像这样的非(有效的)final,并且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实例的引用的副本,并且两个线程最终都"走向房子",在这里。

您可以返回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;
}

注意,在出现虚假唤醒的情况下,应该始终在循环中检查等待条件。此外,如果sum没有在synchronized块之外被访问,则不需要是volatile

最新更新