Почему CyclicBarrier не всегда работает так, как задумано в моей программе? - PullRequest
0 голосов
/ 22 октября 2018

Я сделал простую программу, которая подсчитывает сумму строк в матрице.Я хотел сделать это одновременно, поэтому я использовал 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

Почему это происходит?Я подумал, что это может быть потому, что run () в классе Tmp не обязательно должен быть атомарным, поэтому текущий поток может перейти к подсчету суммы в другой строке, прежде чем устанавливать сумму в 0. Если это так, как я могу предотвратить это?

1 Ответ

0 голосов
/ 03 ноября 2018

Я подумал, что это может быть потому, что run () в классе Tmp не обязательно должен быть атомарным, поэтому текущий поток может перейти к подсчету суммы в другой строке, прежде чем устанавливать сумму в 0.

Я так не думаю, Javadoc CB.await указывает

Если текущий поток является последним потоком, который поступил, и ненулевой барьердействие было предоставлено в конструкторе, тогда текущий поток запускает действие, прежде чем разрешить другим потокам продолжить.

Есть две проблемы, первая - потенциальная многоядерная видимость Поток, выполняющий действие барьера, можетпока не вижу изменения в сумме, потому что оно не обновляется в основном локальном кэше.Кроме того, оператор + = не является атомарным, поэтому он может считывать 20 из суммы, добавляет к ней свои «2» и записывает 22 для суммирования, даже если другой поток изменил значение на 23 между чтением и записью.Чтобы исправить обе проблемы, вам нужно использовать AtomicInteger или Varhandles (volatile только устраняет проблему видимости. Вот дополнительная информация об атомарных переменных

...