Вопрос об условиях гонки: минимальный и максимальный диапазон целого числа - PullRequest
14 голосов
/ 29 сентября 2019

Мне недавно задали этот вопрос в интервью.

Учитывая следующий код, каково будет минимальное и максимальное возможное значение статического целого числа num?

import java.util.ArrayList;
import java.util.List;

public class ThreadTest {
    private static int num = 0;

    public static void foo() {
        for (int i = 0; i < 5; i++) {
            num++;
        }
    }

    public static void main(String[] args) throws Exception{
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Task());
            threads.add(thread);
            thread.start();
        }
        for (int i = 0; i < 5; i++) {
            threads.get(i).join();
        }
        // What will be the range of num ???
        System.out.println(ThreadTest.num);
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        ThreadTest.foo();
    }

}

Я сказал им, что максимальное значение будет 25 (в случае отсутствия условия гонки), а минимальное будет 5 (в случае условия гонки между всеми потоками на каждой итерации).
Но интервьюер сказал, чтоминимальное значение может опуститься даже ниже 5.
Как это возможно?

Ответы [ 3 ]

14 голосов
/ 29 сентября 2019

Я утверждаю, что минимально возможное значение равно 2.

Ключом к этому является неатомичность num++, т. Е. Это чтение и запись, которые могут иметь другие операции между ними.

Вызовите потоки T1..T5:

  • T1 читает 0, T2 читает 0;
  • T1 пишет 1, а затем читает и пишет 3 раза.
  • Затем T2 пишет 1;
  • Затем T1 читает 1;
  • Затем T2-5 выполняет всю свою работу
  • Затем, наконец, T1 пишет 2.

(Примечание: результат 2 не зависит ни от количества потоков, ни от числа итераций, если их не менее 2).

Но честноОтвет на это: это действительно не имеет значения.Существует гонка данных, как определено в JLS 17.4.5 :

Когда программа содержит два конфликтующих доступа (§17.4.1), которые не упорядочены случаем-перед связью говорят, что она содержит гонку данных .

(отсутствует связь между событиями в потоках между и "1035")

Так что вы не можете с пользой полагаться на то, что он делает.Это просто неверный код.

(Более того, я знаю ответ на этот вопрос не из-за с трудом выигранной многопоточной кодовой отладки или глубокого технического чтения: я знаю это, потому что я читал этот ответ раньше, чем где-либо еще.Это хитрый прием, ничего более, и поэтому задание минимального значения не очень хороший вопрос для интервью).

1 голос
/ 29 сентября 2019

Ваши потоки обновляют переменную, которая не является изменчивой, что означает, что она не гарантирует, что каждый поток увидит обновленное значение num.Рассмотрим приведенный ниже поток выполнения потоков:

Thread 1: 0->1->2 (2 iteration left)
Thread 2: 0->1->2->3 (1 iteration left)
Thread 3: 0->1->2->3 (1 iteration left)
Thread 4: 0->1->2->3 (1 iteration left)
Thread 5: 0->1->2->3 (1 iteration left)

В этот момент поток 1 сбрасывает значение 2 num в память, а поток 2,3,4,5 решает прочитать numиз памяти снова (по любой причине).Теперь:

Thread 1: 2->3->4 (completed 2 iteration)
Thread 2: 2->3 (completed 1 iteration)
Thread 3: 2->3 (completed 1 iteration)
Thread 4: 2->3 (completed 1 iteration)
Thread 5: 2->3 (completed 1 iteration)

Поток 1 сбрасывает значение 4 в память, и после этого Theard 2,3,4 .. сбрасывает значение в память, показывая, что текущее значение числа будет 3 вместо 5

0 голосов
/ 29 сентября 2019

Ну, мой ответ будет Макс 25 и Мин 0, так как все ваши операции являются инкрементными, и вы инициализировали его как 0 .. Я думаю, что статический энергонезависимый int брошен туда, чтобы заставить вас перейти к этимМысли о состоянии расы, но есть ли что-нибудь, что могло бы УМЕНЬШИТЬ число в любой ситуации?

Редактировать: Для чего бы это ни стоило, это было бы типичным отвлечением, которое, как они могут ожидать, вы сможете преодолеть вВ реальном мире, оправдывая такую ​​«хитрость», много красных селедок!

...