Java Concurrency - Unterbrechungsrichtlinien

Aug 26 2020

Ich lese Java Concurrency in der Praxis . Im Abschnitt Unterbrechungsrichtlinien im Kapitel

Stornierung und Herunterfahren

Es wird erwähnt

Eine Task sollte nichts über die Unterbrechungsrichtlinie ihres ausführenden Threads annehmen, es sei denn, sie ist explizit für die Ausführung innerhalb eines Dienstes mit einer bestimmten Unterbrechungsrichtlinie ausgelegt. Unabhängig davon, ob eine Aufgabe eine Unterbrechung als Abbruch interpretiert oder eine andere Aktion bei einer Unterbrechung ausführt, sollte darauf geachtet werden, dass der Unterbrechungsstatus des ausführenden Threads erhalten bleibt. Wenn InterruptedException nicht an den Aufrufer weitergegeben wird, sollte der Unterbrechungsstatus nach dem Abfangen von InterruptionException wiederhergestellt werden: Thread.currentThread (). Interrupt ()

Also habe ich versucht, mit dem Auflisten von Beispielen herumzuspielen, um zu verstehen. Aber ich bin verwirrt mit der Ausgabe.

PrimeProducer

public class CorrectPrimeProducer extends Thread {

    private final BlockingQueue<BigInteger> queue;

    public CorrectPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName()+" interrupt status in producer:" + Thread.currentThread().isInterrupted());
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted()) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException e) {
            /* Allow thread to exit */
            Thread.currentThread().interrupt();
            System.out.println(Thread.currentThread().getName()+" interrupt status in producer catch:" + Thread.currentThread().isInterrupted());
        }
    }
}

Hauptmethode ##

public static void main(String[] args) throws InterruptedException {
        BlockingQueue<BigInteger> primes = new LinkedBlockingQueue<>();
        CorrectPrimeProducer generator = new CorrectPrimeProducer(primes);
        generator.start();
        try {
            while (needMorePrimes()) {
                consume(primes.take());
            }
        } finally {
            generator.interrupt();
        }
        TimeUnit.SECONDS.sleep(5);
        System.out.println(generator.getName()+" interrupt status in main:"+generator.isInterrupted());
    }

    //do something
    private static void consume(BigInteger take) {
        System.out.println(take);
    }

    private static int counter = 1;

    private static boolean needMorePrimes() {
        counter++;
        if(counter == 10){
// after counter reaches 10 return false
            return false;
        }
        return true; 
    }

Ausgabe:

// when TimeUnit.SECONDS.sleep(5); in main class is not commented

Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in producer catch:true
Thread-0 interrupt status in main:false
//When TimeUnit.SECONDS.sleep(5); in main class is commented
Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in main:true
Thread-0 interrupt status in producer catch:true

Frage

  1. Nur durch Hinzufügen von TimeUnit.SECONDS.sleep (5) im Hauptthread der Hauptklasse. Der Interrupt-Status des ausführenden Threads (dh des Generators) wird zurückgesetzt. Wenn ich die TimeUnit.SECONDS.sleep (5) -Methode kommentiere, bleibt in diesem Fall der Interrupt-Status erhalten. Warum und wie passiert das?

  2. Im Buch wird erwähnt, dass ein Thread nur von seinem Besitzer unterbrochen werden sollte. Hier im obigen Beispiel, wer ist der Eigentümer? Ich denke, sein Hauptmethoden-Thread.

Antworten

1 Joni Aug 26 2020 at 04:25

Durch Hinzufügen geben TimeUnit.SECONDS.sleep(5)Sie genügend Zeit, damit der Thread beendet werden kann.

Wenn ein Thread beendet wird, wird sein Interrupt-Flag gelöscht.

Dies ist in der Spezifikation nicht dokumentiert, aber es ist das, was passiert. Siehe zum Beispiel diesen Fehlerbericht :

Da hier keine Spezifikation verletzt wird, habe ich dies eher zu einer Verbesserungsanforderung als zu einem Fehler gemacht. Wahrscheinlich ist das Fehlen einer Spezifikation ein Fehler - wir haben absichtlich angegeben, dass "Interrupt nach Beendigung keine Auswirkungen haben muss", um die Tatsache zu behandeln, dass der Interrupt-Status in der VM gespeichert ist und nach Beendigung eines Threads nicht mehr existiert. Wir haben es jedoch versäumt, dies in der Thread.isInterrupted-Spezifikation wiederzugeben.

Ohne das Extra sleepvermute ich, dass Sie theoretisch beides sehen trueund den falseStatus unterbrechen könnten, weil es eine Rennbedingung gibt, aber es ist weitaus wahrscheinlicher, dass Sie es truedank der Thread-Planung sehen werden. Das Zeitfenster, in dem der Interrupt-Status zwischen der ausgelösten Ausnahme und der Wiederherstellung des Interrupt-Status im catch-Block falsch ist, ist unglaublich klein.

2 michalk Aug 26 2020 at 03:49

Nur durch Hinzufügen von TimeUnit.SECONDS.sleep (5) im Hauptthread der Hauptklasse. Der Interrupt-Status des ausführenden Threads (dh des Generators) wird zurückgesetzt. Wenn ich die TimeUnit.SECONDS.sleep (5) -Methode kommentiere, bleibt in diesem Fall der Interrupt-Status erhalten. Warum und wie passiert das?

Sie verwenden keinen Synchronisationsmechanismus (außer der Blockierungswarteschlange) zwischen dem Hauptthread und dem CorrectPrimeProducer, wenn der Hauptthread den Status druckt - CorrectPrimeProducerder unterbrochene Status wurde möglicherweise noch nicht beibehalten (durch Ausführen von catchBlockierungsanweisungen), sodass Sie falseals Ergebnis erhalten.

Wenn Sie dem Hauptthread hinzufügen sleep, Threaderhöhen Sie einfach die Wahrscheinlichkeit, dass der CorrectPrimeProducerThread den Unterbrechungsstatus beibehält, indem Sie Blockanweisungen aufrufen catch, bevor der Hauptthread versucht, seinen Status zu drucken. Deshalb wird gedruckt true.

Im Buch wird erwähnt, dass ein Thread nur von seinem Besitzer unterbrochen werden sollte. Hier im obigen Beispiel, wer ist der Eigentümer? Ich denke, sein Hauptmethoden-Thread.

In diesem Fall sind Sie der Eigentümer (der Eigentümer ist der Code, der den Thread erstellt) des CorrectPrimeProducerThreads, sodass Sie entscheiden, was eine Unterbrechung für ihn bedeutet. Sie können es beispielsweise neu erstellen, wenn es unterbrochen wurde (dies geschieht beispielsweise Threadstandardmäßig für s aus Java-Thread-Pools).