繁体   English   中英

在java中的屏障线程之间共享局部变量值

[英]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; }
}

这有效,但我听说有一种方法可以将sumoldSum (或至少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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM