Współbieżność Java - zasady przerywania
Czytam współbieżność języka Java w praktyce . W sekcji Zasady dotyczące zakłóceń w rozdziale
Anulowanie i wyłączenie
Jest wspomniany
Zadanie nie powinno niczego zakładać na temat zasad przerywania wątku wykonawczego, chyba że zostało wyraźnie zaprojektowane do uruchamiania w ramach usługi, która ma określoną politykę przerywania. Niezależnie od tego, czy zadanie interpretuje przerwanie jako anulowanie, czy podejmuje inną akcję po przerwaniu, powinno zadbać o zachowanie stanu przerwania wątku wykonującego. Jeśli nie zamierza propagować wyjątku InterruptedException do obiektu wywołującego, powinien przywrócić stan przerwania po przechwyceniu wyjątku InterruptionException: Thread.currentThread (). Przerwać ()
Więc spróbowałem pobawić się z listą próbek, aby zrozumieć. Ale jestem zdezorientowany z wyjściem.
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());
}
}
}
główna metoda ##
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;
}
Wynik:
// 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
Pytanie
Wystarczy dodać TimeUnit.SECONDS.sleep (5) w głównym wątku w głównej klasie. Stan przerwania wykonywania wątku (tj. Generatora) jest resetowany. Jeśli skomentuję metodę TimeUnit.SECONDS.sleep (5), wówczas stan przerwania zostanie zachowany. Dlaczego tak się dzieje i jak?
W książce jest napisane. Wątek powinien być przerywany tylko przez jego właściciela. Kto w powyższym przykładzie jest właścicielem? Myślę, że to główny wątek metody.
Odpowiedzi
Dodając TimeUnit.SECONDS.sleep(5), dajesz wystarczająco dużo czasu na zakończenie wątku.
Kiedy wątek się kończy, jego flaga przerwania jest czyszczona.
Nie jest to udokumentowane w specyfikacji, ale tak się dzieje. Zobacz na przykład ten raport o błędzie :
Żadna specyfikacja nie została tutaj naruszona, więc zgłosiłem to jako prośbę o ulepszenie, a nie błąd. Prawdopodobnie brak specyfikacji jest błędem - celowo określiliśmy, że „przerwanie po zakończeniu nie musi mieć żadnego wpływu”, aby poradzić sobie z faktem, że stan przerwania jest przechowywany w maszynie wirtualnej i nie istnieje już po zakończeniu wątku. Jednak zaniedbaliśmy odzwierciedlenie tego w specyfikacji Thread.isInterrupted.
sleepPodejrzewam, że bez dodatku , teoretycznie można było zobaczyć oba truei falsestatus przerwania, ponieważ istnieje warunek wyścigu, ale jest o wiele bardziej prawdopodobne, że zobaczysz je truedzięki planowaniu wątków. Okno czasowe, w którym stan przerwania jest fałszywy, między wyrzuceniem wyjątku a przywróceniem stanu przerwania w bloku catch, jest niewiarygodnie małe.
Wystarczy dodać TimeUnit.SECONDS.sleep (5) w głównym wątku w głównej klasie. Stan przerwania wykonywania wątku (tj. Generatora) jest resetowany. Jeśli skomentuję metodę TimeUnit.SECONDS.sleep (5), wówczas stan przerwania zostanie zachowany. Dlaczego tak się dzieje i jak?
Nie używasz żadnego mechanizmu synchronizacji (poza blokowaniem kolejki) między głównym wątkiem a CorrectPrimeProducertak, gdy główny wątek drukuje status - być CorrectPrimeProducermoże nie zachował jeszcze stanu przerwania (wykonując catchinstrukcje blokowe), więc otrzymasz falsewynik.
Dodając sleepdo głównego wątku Thread, po prostu zwiększasz prawdopodobieństwo, że CorrectPrimeProducerwątek zachowa stan przerwania, wywołując catchinstrukcje blokowe, zanim główny wątek spróbuje wydrukować swój status. Dlatego drukuje true.
W książce jest napisane. Wątek powinien być przerywany tylko przez jego właściciela. Kto w powyższym przykładzie jest właścicielem? Myślę, że to główny wątek metody.
W tym przypadku jesteś właścicielem (właścicielem jest kod, który tworzy wątek) CorrectPrimeProducerwątku, więc decydujesz, co oznacza dla niego przerwanie. Na przykład możesz go odtworzyć, jeśli został przerwany (dzieje się tak na przykład w przypadku Threads z pul wątków Java).