0

我一直在努力实现一个自定义的循环障碍,它添加传递给 await 方法的值,并在调用 notify 后将总和返回给所有线程。

编码:

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。然而,在我绞尽脑汁之后,我看不到任何办法。

有可能吗?如果有,怎么做?

4

2 回答 2

0

您可以返回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则不需要。volatilesynchronized

于 2021-10-25T20:07:53.720 回答
0

然而,在我绞尽脑汁之后,我看不到任何办法。

这么。

有可能吗?如果有,怎么做?

这不可能。

对于一些证据:

尝试将本地 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 实例的引用的副本,并且两个线程最终都在这里“走到房子”。

于 2021-10-25T20:11:52.353 回答