Реализовал Классический пример производителя и потребителя. Здесь производитель будет спать в течение 10 секунд после создания value = 0 [не будет ждать состояния, поскольку размер очереди меньше 10]. И потребитель будет использовать value =0 и уведомить производителя, что он будет спать в течение секундочку.
Итак, мой вопрос в том, почему уведомление потребителя не прерывает поток производителя и не печатает Producer Exception cached.
Результат следующей программы выглядит так:
Producer add value=0
Consumer consumes value=0
(подождите 10 секунд)
Producer add value=1
Consumer consumes value=1
(подождите 10 секунд)
Producer add value=2
Consumer consumes value=2
Классический образец производителя и потребителя.
public class ClassicalProducerConsumer{
public static void main(String[] args) {
Buffer buffer = new Buffer(3);
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
try {
int value = 0;
while (true) {
buffer.add(value);
value++;
Thread.sleep(10000); // Make producer wait for 10 seconds.
}
}catch (Exception ex){
System.out.println("Producer Exception cached");
ex.printStackTrace();
}
}
});
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
int value = buffer.poll();
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("Consumer Exception cached");
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}
class Buffer{
Queue<Integer> queue;
int size;
public Buffer(int size) {
this.size = size;
queue = new LinkedList<>();
}
public void add(int value) throws InterruptedException {
synchronized (this){
while (queue.size() >=size){
wait();
}
System.out.println("Producer add value = "+ value);
queue.add(value);
notify();
}
}
public int poll() throws InterruptedException {
synchronized (this){
while (queue.size()==0){
wait();
}
int value = queue.poll();
System.out.println("Consumer consumes value = "+ value);
notify();
return value;
}
}
}
Поскольку для такой проблемы производителя / потребителя может быть несколько потоков, я обычно предпочитаю использовать notifyAll() только для того, чтобы убедиться, что любой возможный поток не будет пропущен или пропущен.




Как упоминал @RealSkeptic, notify() не имеет ничего общего с прерыванием. Вместо этого вызовите interrupt() в потоке, который вы хотите прервать. Тогда любой метод блокировки в этом потоке вернет управление, выбрасывая Прерванное исключение. Рекомендуется восстановить прерванное состояние потока, вызвав Thread.currentThread().interrupt() в блоке catch.
Нет необходимости восстанавливать прерванное состояние. Это часто повторяемый бесполезный совет.
@JohnKugelman что, если мне нужно проверить, был ли поток прерван или нет позже в коде?
@JohnKugelman Брайан Гетц в своей книге «Параллелизм на практике» говорит, что есть два способа справиться с прерыванием: распространить его выше по стеку или восстановить. Поскольку функция Run () в потоке не допускает распространения, нам нужно восстановить ее, тогда
Вы узнаете, в зависимости от того, находитесь ли вы в блоке try или catch. Флаг не должен вам об этом сообщать. Если вы поймаете SQLException или NumberFormatException, вы используете поток кода, чтобы определить, находитесь ли вы в исключительном состоянии; вам не нужно устанавливать или проверять флаг.
Код, который вы пишете, - это единственный код, который заботится о статусе. Как только вы получите InterruptedException, вы получите уведомление о прерывании. Вам больше никому не нужно передавать эту информацию. Флаг используется только в том случае, если вы выполняете какую-либо длительную задачу без вызова функции блокировки, такой как sleep(). Нет необходимости повторно звонить в interrupt(), и нет причин звонить в isInterrupted(). Можно безопасно обрабатывать InterruptedException и / или проверять interrupted() и позволять сбросить флаг.
@JohnKugelman Хорошо, я понимаю вашу точку зрения и согласен с вами, но рассмотрим один конкретный сценарий: run() потока вызывает метод (назовем его calculate()), который должен внутренне реализовывать свою политику прерывания и, следовательно, перехватывать прерывание исключения по любым причинам (например: для регистрировать определенные локальные переменные в этом методе). Здесь run() ничего не знает о прерывании. Но, возможно, это нужно знать. Конечно, вы можете повторно вызвать прерванное исключение из блока catch calculate(), но восстановление статуса прерывания тоже может быть хорошим способом.
В этом сценарии я бы задокументировал, что calculate() - это медленный / блокирующий метод, который можно прервать в его Javadoc, и объявил бы его с помощью throws InterruptedException.
Я не хочу спорить, и на самом деле мои мысли о перебоях заслуживают отдельной публикации в блоге или, может быть, отдельных вопросов и ответов. Я думаю, что документация по API плохо объясняет, как следует использовать прерывания и для чего они нужны. Вы может сохраняете флаг прерывания, а вы может проверяете его, не очищая, но ни то, ни другое не требуется. Документация по API должна отстаивать простой поиск InterruptedException и, в редких случаях, проверку interrupted(). Это должно препятствовать звонку в isInterrupted(). Сброс флага прерывания нормальный и ожидаемый.
Самое главное, он должен объяснить, кто на самом деле вызывает interrupt() и вызывает прерывания: это всегда ваш собственный код! Прерывания не случаются, если вы сами, автор приложения, не заставляете их происходить. Если вы сами не вызовете interrupt(), ваши потоки никогда не будут прерваны. Ошибка API в том, что InterruptedException является проверенным исключением. Требование ловить его каждый раз, когда мы звоним в sleep(), является ошибкой. Большинство из нас не прерывают наши собственные потоки; наши звонки во сне никогда не прерываются. Мы обязаны обрабатывать исключения, которых никогда не бывает! Фу.
notifyиinterrupt- два разных механизма.