java - Interviewfrage zu Race Condition: Min- und Max-Bereich einer ganzen Zahl




multithreading race-condition (2)

Diese Frage wurde mir kürzlich in einem Interview gestellt.

Welcher Wert für die statische Ganzzahl num angesichts des folgenden Codes min und max möglich?

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();
    }

}

Ich sagte ihnen, dass der Maximalwert 25 sein würde (falls es keine Racebedingung gibt) und min 5 sein würde (falls es eine Racebedingung zwischen allen Threads bei jeder Iteration gibt).
Der Interviewer sagte jedoch, dass der Mindestwert sogar unter 5 sinken kann.
Wie ist das möglich?


Ich behaupte, der minimal mögliche Wert ist 2.

Der Schlüssel dazu ist die Nichtatomarität von num++ , dh es handelt sich um ein Lesen und ein Schreiben, zwischen denen möglicherweise andere Operationen ausgeführt werden.

Rufe die Threads T1..T5 auf:

  • T1 liest 0, T2 liest 0;
  • T1 schreibt 1 und liest und schreibt dann 3 Mal.
  • Dann schreibt T2 1;
  • Dann lautet T1 1;
  • Dann erledigen T2-5 ihre ganze Arbeit
  • Dann schreibt T1 endlich 2.

(Hinweis: Das Ergebnis 2 ist weder von der Anzahl der Threads noch von der Anzahl der Iterationen abhängig, sofern mindestens zwei vorhanden sind.)

Aber die ehrliche Antwort darauf lautet: Es ist wirklich egal. Es gibt ein Datenrennen gemäß JLS 17.4.5 :

Wenn ein Programm zwei widersprüchliche Zugriffe (§17.4.1) enthält, die nicht nach einer Vor-Ereignis-Beziehung geordnet sind, enthält es ein Datenrennen.

(Es gibt kein Vorkommnis zwischen den Aktionen in den Threads)

Sie können sich also nicht auf das verlassen, was es tut. Es ist einfach falscher Code.

(Außerdem kenne ich die Antwort auf diese Frage nicht aufgrund eines hart erkämpften Debuggens von Multithread-Code oder einer gründlichen technischen Lektüre. Ich weiß das, weil ich diese Antwort zuvor an anderer Stelle gelesen habe. Es ist ein Salon-Trick, nichts weiter und frage die Mindestwert ist keine sehr gute Interviewfrage).


Ihre Threads aktualisieren eine Variable, die nicht flüchtig ist, was bedeutet, dass nicht garantiert wird, dass jeder Thread den aktualisierten Wert von num sieht. Betrachten wir den folgenden Ablauf der Ausführung von Threads:

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)

Zu diesem Zeitpunkt löscht Thread 1 den Wert 2 von num in den Speicher und Thread 2,3,4,5 entscheidet sich (aus irgendeinem Grund) erneut, die num aus dem Speicher zu lesen. Jetzt:

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)

Thread 1 spült den Wert 4 in den Speicher und danach zeigt Theard 2,3,4 .., dass der aktuelle Wert der Zahl 3 statt 5





race-condition