Где поймать InterruptedException для Object.wait () с синхронизированным блоком?

Насколько я понимаю, это очень распространенный сниппет для многопоточности в Java.

boolean loaded = false;
Object lock = new Object();

public void block() {
    synchronized (lock) {
        while(!loaded)
            lock.wait(); // Unhandled exception type InterruptedException
    }
}

и в другой ветке

public void done () {
    synchronized (lock) {
        loaded = true;
        lock.notify();
    }
}

Но я не уверен, где мне поставить «попробовать и поймать», могу ли я окружить весь синхронизированный блок или только lock.wait()? Что такое эмпирическое правило и действительно ли оно имеет значение?

Для обработки можно ли в этой ситуации позвонить в Thread.currentThread().interrupt()?


person Nikolay Kuznetsov    schedule 09.01.2013    source источник
comment
A) Если ваш поток получил прерывание, наиболее разумным вариантом будет вызвать Thread.currentThread (). Interrupt () и выйти. B) Вы должны использовать примитивы синхронизации высокого уровня - например, очереди C) Вы должны использовать notifyAll вместо notify D) вам следует прочитать какую-нибудь книгу о многопоточности, например, Java Concurrency In Practice от Goetz   -  person Boris Treukhov    schedule 09.01.2013
comment
лично, когда я использую эти механизмы (см. комментарий @Boris Treukhov выше), я сразу помещаю уловку вокруг вызова wait ()   -  person radai    schedule 09.01.2013
comment
@BorisTreukhov, не могли бы вы подробнее рассказать об этом? Но я не уверен, что размещение проверенного исключения для всех методов в стеке - лучшее решение. Я не совсем понимаю, что это значит   -  person Nikolay Kuznetsov    schedule 09.01.2013
comment
Ответ Андрея правильный, но я думаю, что распространение исключения с помощью ключевого слова throws не всегда лучший выбор, потому что вы можете захотеть выполнить некоторую логику выключения (я имею в виду нечто большее, чем просто закрытие ресурсы, которые выполняются в конструкции finally или try-with) - проблема в том, что в конечном итоге зарезервировано для других исключительных ситуаций (не только прерванных исключений), и некоторая логика делает не очень хорошо вписывается - поэтому клиентский код (и его поток) будет менее читабельным.   -  person Boris Treukhov    schedule 09.01.2013
comment
Просто перефразирую - когда вы разрабатываете примитив синхронизации, вы не можете делать ничего, кроме распространения исключения (примитив не имеет дополнительной логики), но когда вы пишете код с несколькими потоками, используя распространение с throws заставит вас разработать свою логику для перехвата InterruptedException, но InterruptedException, скорее всего, возникает только при завершении работы JVM. Да, есть некоторые ситуации, когда разработка программы вокруг исключения в порядке (например, TimeoutException), но я думаю, что это не решение по умолчанию.   -  person Boris Treukhov    schedule 09.01.2013


Ответы (2)


Возможно, вам стоит рассмотреть статью Брайана Гетца: Теория и практика Java : Работа с InterruptedException

В общем, если вы не знаете, что делать, ничего не делайте, но дайте знать коду вызывающего абонента, что этот метод может быть прерван во время ожидания блокировки. Вы можете сделать это, добавив throws InterruptedException к сигнатуре метода.

Если по какой-то причине вы вынуждены добавить блок try / catch и обработать исключение, подойдет Thread.currentThread (). Interrupt (), но его повторная генерация может быть как можно более подходящим.

person Andrey Taptunov    schedule 09.01.2013
comment
Да, я добавил эту статью в закладки, прежде чем писать вопрос. Но мой главный вопрос выделен жирным шрифтом. - person Nikolay Kuznetsov; 09.01.2013
comment
@NikolayKuznetsov это не имеет особого значения, изменения в потоке программы, вызванные исключением, не имеют ничего общего с многопоточностью. Важно то, что статус прерывания не должен быть утерян. Но я не уверен, что размещение проверенного исключения для всех методов в стеке - лучшее решение. P.S. Еще одна вещь: вызов notify () вместо notifyAll () всегда требует некоторых рассуждений о безопасности. - person Boris Treukhov; 09.01.2013

Но я не уверен, где мне поставить try and catch, я могу окружить весь синхронизированный блок или только lock.wait ()? Что такое эмпирическое правило и действительно ли оно имеет значение?

lock.wait() - единственная строка, которая может вызвать исключение в вашем block методе. Итак, поместите ли вы свой try / catch вокруг ожидания или включите все тело метода, не имеет значения с точки зрения выполнения (при условии, что вы выйдете из цикла в обоих случаях, конечно - если вы хотите продолжить цикл, тогда блок try / catch очевидно, должен быть внутри цикла).

Другими словами, это всего лишь вопрос стиля - см. этот другой обсуждение. Я лично считаю, что это более читабельно:

try {
    synchronized (lock) {
        while (!loaded) {
            lock.wait(); // Unhandled exception type InterruptedException
        }
    }
} catch (InterruptedException e) {
    //to ignore me or not to ignore me, that is the question
    Thread.currentThread().interrupt();
}

Можно ли для обработки вызывать Thread.currentThread (). interrupt () в этой ситуации?

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

new Thread(new Runnable() {

    @Override
    public void run() {
        block();
    }
}).start();

Во втором примере ниже вы обязательно должны повторно прервать поток, чтобы дать методу run знать, была ли причина выхода block() в том, что загруженный стал истинным или что он был прерван:

public void run() {
    while(!Thread.currentThread().isInterrupted()) {
        loaded = false;
        System.out.println("Launching data load");
        block(); //if false has become true, loop, if interrupted, exit
    }
}

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

person assylias    schedule 09.01.2013
comment
Я думаю, что этот учебник по уведомлению потока лучше: tutorials.jenkov.com/java- concurrency / thread-signaling.html - person android developer; 03.05.2014