为什么CyclicBarrier不能始终按照我的程序的预期运行?

时间:2018-10-22 19:17:42

标签: concurrency java.util.concurrent

我制作了一个简单的程序,用于计算矩阵中的行总数。我想同时进行,所以我使用了CyclicBarrier。有时它会按预期工作,但有时会出现一些小错误,就像程序遗漏了一个或两个数字一样。

这是我的代码:

package synchronizacja;
import java.util.concurrent.CyclicBarrier;
public class SumyWierszamiSekwencyjnie {
private static final int ROWS = 10;
private static final int COLUMNS = 100;
private static  int sum;
private static CyclicBarrier barrier = new CyclicBarrier(COLUMNS, new Tmp());

private static class Tmp implements Runnable {
    @Override
    public void run() {
        System.out.println("The sum is: " + sum);
        sum = 0;
    }
}

private static class CountByColumns implements Runnable {
    private void CriticalSection(int wiersz) {
        sum += wiersz;
    }
    @Override
    public void run() {
        for (int i = 0; i < ROWS; i++) {
           CriticalSection(i);
           try { barrier.await(); }
           catch (Exception e) { System.out.println("Exception"); }
        }

    }
}

public static void main(String[] args) {
    for (int i = 0; i < COLUMNS; i++) {
        new Thread(new CountByColumns()).start();
    }
}
}

输出应为

The sum is: 0
The sum is: 100
The sum is: 200
The sum is: 300
The sum is: 400
The sum is: 500
The sum is: 600
The sum is: 700
The sum is: 800
The sum is: 900

有时候是这样,但更多时候它显示类似

The sum is: 0
The sum is: 100
The sum is: 200
The sum is: 297
The sum is: 400
The sum is: 500
The sum is: 600
The sum is: 700
The sum is: 800
The sum is: 900

为什么会发生?我在想这可能是因为Tmp类中的run()不必是原子的,所以当前线程可能会在将sum设置为0之前跳到对另一行的总和进行计数。

1 个答案:

答案 0 :(得分:0)

  

我当时想这可能是因为Tmp类中的run()不必是原子的,所以当前线程可能会在将sum设置为0之前跳过对另一行的总和的计数。

我不这么认为,the javadoc of CB.await指定

  

如果当前线程是最后到达的线程,并且在构造函数中提供了非null屏障操作,则当前线程将在允许其他线程继续运行之前运行该操作。

有两个问题,第一个是潜在的多核可见性 执行屏障操作的线程可能尚未看到总和的变化,因为它没有在核心本地缓存中更新。 另外,+ =运算符不是原子的,因此即使另一个线程将读写之间的值更改为23,它也可能会从sum中读取20并将其“ 2”加到它并写入22以求和。 要解决这两个问题,您需要使用AtomicInteger或Varhandles(volatile仅可解决可见性问题。Here are more information about atomic variables