powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / LogWrite пример из Concurrency in practice. Где тут race condition?
17 сообщений из 17, страница 1 из 1
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408201
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Собственно пример из книжки:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
public class LogWriter {
    private final BlockingQueue<String> queue;
    private final LoggerThread logger;
    private static final int CAPACITY = 1000;

    public LogWriter(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>(CAPACITY);
        this.logger = new LoggerThread(writer);
    }

    public void start() {
        logger.start();
    }

    public void log(String msg) throws InterruptedException {
        queue.put(msg);
    }

    private class LoggerThread extends Thread {
        private final PrintWriter writer;

        public LoggerThread(Writer writer) {
            this.writer = new PrintWriter(writer, true); // autoflush
        }

        public void run() {
            try {
                while (true)
                    writer.println(queue.take());
            } catch (InterruptedException ignored) {
            } finally {
                writer.close();
            }
        }
    }
}



и мы хотим научиться стопать эту машину.
Тут у нас много продюсеров и всего один consumer.
Дальше идёт предложение сделать флаг, который будет прекращать добавление новых сообщений в очередь и приводится вот такой вот код:

Код: java
1.
2.
3.
4.
5.
6.
public void log(String msg) throws InterruptedException {
     if(!shutdownRequested)
           queue.put(msg);
     else
           throw new IllegalArgumentException("logger is shut down");
 }



и такой вот текст:
авторAnother approach to shutting down LogWriter would be to set a “shutdown requested” flag to prevent further messages from being submitted, as shown in Listing 7.14. The con- sumer could then drain the queue upon being notified that shutdown has been requested, writing out any pending messages and unblocking any producers blocked in log . However, this approach has race conditions that make it unreliable. The implementation of log is a check-then-act sequence: producers could observe that the service has not yet been shut down but still queue messages after the shutdown, again with the risk that the producer might get blocked in log and never become unblocked. There are tricks that reduce the likelihood of this (like having the consumer wait several seconds before declaring the queue drained), but these do not change the fundamental problem, merely the likelihood that it will cause a fail- ure.


Я не понял почему там race condition. В чем проблема я тоже не понял. А также хотелось бы понять продюсеры могут залочиться?


P.S.

Чего-то тут совсем никак не перевести
автор producers could observe that the service has not yet been shut down but still queue messages after the shutdown, again with the risk that the producer might get blocked in log and never become unblocked.

Продюсеры могут наблюдать, что сервис, который ещё не выключен до сих пор набирает сообщения в очередь(wtf?), вновь есть риск, что продюссер может залочиться и никогда не разлочиться.

Ничего не понял из этого предложения.
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408206
base2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
questioner, race condition может быть такой:
Код: java
1.
2.
3.
4.
5.
6.
public void log(String msg) throws InterruptedException {
     if(!shutdownRequested) //--> в этот момент shutdown еще не requested
           queue.put(msg); //--> а в этот момент shutdown уже requested, но текущий producer-тред об этом уже не узнает
     else
           throw new IllegalArgumentException("logger is shut down");
}
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408207
base2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
questionerЧего-то тут совсем никак не перевести
автор producers could observe that the service has not yet been shut down but still queue messages after the shutdown, again with the risk that the producer might get blocked in log and never become unblocked.

Продюсеры могут наблюдать, что сервис, который ещё не выключен до сих пор набирает сообщения в очередь(wtf?), вновь есть риск, что продюссер может залочиться и никогда не разлочиться.

Ничего не понял из этого предложения.
Продьюсеры могут видеть, что сервис еще не выключен, но при этом все равно получится так, что они добавят сообщения в очередь, когда он таки уже будет выключен.
Залочиться же продьюсеры могут, насколько я понимаю, в случае, если logger thread завершился, а очередь при этом уже успели забить новыми сообщениями, через race condition, не зная о том, что shutdown requested.
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408310
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
base2questioner, race condition может быть такой:
Код: java
1.
2.
3.
4.
5.
6.
public void log(String msg) throws InterruptedException {
     if(!shutdownRequested) //--> в этот момент shutdown еще не requested
           queue.put(msg); //--> а в этот момент shutdown уже requested, но текущий producer-тред об этом уже не узнает
     else
           throw new IllegalArgumentException("logger is shut down");
}



Согласен, что это race condition.

В принципе может быть, что между этими двумя строками LoggerThread проверит, что в очереди ничего нет и флаг включен и можно вырубаться в таком случае, а на самом деле сообщения ещё есть


а по поводу того почему продюсеры блокируются не понял. queue.put неблокирующий метод. Он блокируется только в том случае, если очередь переполнена.

ведь LoggerThread в одном потоке разгребает очередь, разве такое возможно, что он успеет разгрести всю очередь до того, как потоки разблокируются и вставят новое значение в очередь?
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408459
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В дополнение ещё по этой теме хотел бы понять почему автор выбрал правильное решение такое:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
public class LogService {
    private final BlockingQueue<String> queue;
    private final LoggerThread loggerThread;
    private final PrintWriter writer;
    @GuardedBy("this") private boolean isShutdown;
    @GuardedBy("this") private int reservations;

    public LogService(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>();
        this.loggerThread = new LoggerThread();
        this.writer = new PrintWriter(writer);
    }

    public void start() {
        loggerThread.start();
    }

    public void stop() {
        synchronized (this) {
            isShutdown = true;
        }
        loggerThread.interrupt();
    }

    public void log(String msg) throws InterruptedException {
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException(/*...*/);
            ++reservations;
        }
        queue.put(msg);
    }

    private class LoggerThread extends Thread {
        public void run() {
            try {
                while (true) {
                    try {
                        synchronized (LogService.this) {
                            if (isShutdown && reservations == 0)
                                break;
                        }
                        String msg = queue.take();
                        synchronized (LogService.this) {
                            --reservations;
                        }
                        writer.println(msg);
                    } catch (InterruptedException e) { /* retry */
                    }
                }
            } finally {
                writer.close();
            }
        }
    }
}



Тут 3 синхронайзд блока и ещё добавлен каунтер reservations

такой вариант будет небезопасным или может будет работать медленнее?

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
public class LogService {
    private final BlockingQueue<String> queue;
    private final LoggerThread loggerThread;
    private final PrintWriter writer;
    @GuardedBy("this")
    private boolean isShutdown;


    public LogService(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>();
        this.loggerThread = new LoggerThread();
        this.writer = new PrintWriter(writer);
    }

    public void start() {
        loggerThread.start();
    }

    public void stop() {
        synchronized (this) {
            isShutdown = true;
        }
        loggerThread.interrupt();
    }

    public void log(String msg) throws InterruptedException {
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException(/*...*/);
            queue.put(msg); // inside synchronized now
        }

    }

    private class LoggerThread extends Thread {
        public void run() {
            try {
                while (true) {
                    try {
                        synchronized (LogService.this) {
                            if (isShutdown && queue.isEmpty())
                                break;
                        }
                        String msg = queue.take();
                        writer.println(msg);
                    } catch (InterruptedException e) { /* retry */
                    }
                }
            } finally {
                writer.close();
            }
        }
    }
}
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408476
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
questionerтакой вариант будет небезопасным или может будет работать медленнее?
Код: java
1.
2.
3.
4.
5.
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException(/*...*/);
            queue.put(msg); // можем ждать пока разгребется очередь
        }


Код: java
1.
2.
3.
4.
5.
                        synchronized (LogService.this) { //ждем пока отпустит код приведенный выше
                            if (isShutdown && queue.isEmpty())
                                break;
                        }
                        String msg = queue.take(); // чтобы разгрести


Так что на добавлении 1001 msg возможен deadlock
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408534
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сергей Арсеньевquestionerтакой вариант будет небезопасным или может будет работать медленнее?
Код: java
1.
2.
3.
4.
5.
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException(/*...*/);
            queue.put(msg); // можем ждать пока разгребется очередь
        }


Код: java
1.
2.
3.
4.
5.
                        synchronized (LogService.this) { //ждем пока отпустит код приведенный выше
                            if (isShutdown && queue.isEmpty())
                                break;
                        }
                        String msg = queue.take(); // чтобы разгрести


Так что на добавлении 1001 msg возможен deadlock

Не понял.

Два потока понятно какие.

а где 2 ресурса? и где они захватываются в разном порядке?
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408539
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
хотя понял, видимо требование про порядок - необязательное. просто как вариант дедлока
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408544
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
questioner,

очередь и монитор. Внутри монитора писатель в очередь будет ждать при переполнении ее разгребания, а разгребатель будет ждать монитора. Или я что-то не так понял?
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408712
base2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
questionerа по поводу того почему продюсеры блокируются не понял. queue.put неблокирующий метод. Он блокируется только в том случае, если очередь переполнена.

ведь LoggerThread в одном потоке разгребает очередь, разве такое возможно, что он успеет разгрести всю очередь до того, как потоки разблокируются и вставят новое значение в очередь?

В общем случае у тебя очередь не обязательно из 1000 элементов.
Предположим, что она ограничена двумя и представим ситуацию, когда очередь в данный момент пуста и мы выставляем shutdownRequested в true. LoggerThread успевает это увидеть, проверяет, что очередь пуста и завершается. В то же время, скажем, 3 producer-треда попадают на put через гонку - 2 из них положат свои значения в очередь, а третий заблокируется навсегда, так как освобождать очередь уже некому.
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408727
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
public class LogService {
    private final BlockingQueue<String> queue;
    private final LoggerThread loggerThread;
    private final PrintWriter writer;
    @GuardedBy("this") private boolean isShutdown;
    @GuardedBy("this") private int reservations;

    public LogService(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>();
        this.loggerThread = new LoggerThread();
        this.writer = new PrintWriter(writer);
    }

    public void start() {
        loggerThread.start();
    }

    public void stop() {
        synchronized (this) {
            isShutdown = true;
        }
        loggerThread.interrupt();
    }

    public void log(String msg) throws InterruptedException {
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException(/*...*/);
            ++reservations;
        }
        queue.put(msg);
    }

    private class LoggerThread extends Thread {
        public void run() {
            try {
                while (true) {
                    try {
                        synchronized (LogService.this) {
                            if (isShutdown && reservations == 0)
                                break;
                        }
                        String msg = queue.take();
                        synchronized (LogService.this) {
                            --reservations;
                        }
                        writer.println(msg);
                    } catch (InterruptedException e) { /* retry */
                    }
                }
            } finally {
                writer.close();
            }
        }
    }
}


Кстати, этот код не совсем будет корректно работать, если дойдет до макс размера (ну или через конструктор указать размер фиксированный очереди). Если reservations будет не 0, а очередь пуста, то сколько бы не вызывать shutdown() он не сработет. А может это быть в таком случае: фикс размер очереди, например 1, два потока добавляют 2 элемента, теперь reservations == 2, далее один из них интерраптится на put(), в результате в очереди по факту 1 элемент, а reservations == 2. Теперь shutdown() не сработает никогда.
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408736
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сергей Арсеньевquestioner,

очередь и монитор. Внутри монитора писатель в очередь будет ждать при переполнении ее разгребания, а разгребатель будет ждать монитора. Или я что-то не так понял?

Не, всё верно. Я почему-то думал, что дедлок это обязательно захват мониторов в разных порядках.

а тут у нас для того, чтобы добавить/освободить очередь нужно 1. захватить монитор 2. дождаться пока блокирующий метод исполнится. и собственно если монитор занят тредом, который не может добавить, то тред который разгребает ничего не сможет сделать)
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408742
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
base2questionerа по поводу того почему продюсеры блокируются не понял. queue.put неблокирующий метод. Он блокируется только в том случае, если очередь переполнена.

ведь LoggerThread в одном потоке разгребает очередь, разве такое возможно, что он успеет разгрести всю очередь до того, как потоки разблокируются и вставят новое значение в очередь?

В общем случае у тебя очередь не обязательно из 1000 элементов.
Предположим, что она ограничена двумя и представим ситуацию, когда очередь в данный момент пуста и мы выставляем shutdownRequested в true. LoggerThread успевает это увидеть, проверяет, что очередь пуста и завершается. В то же время, скажем, 3 producer-треда попадают на put через гонку - 2 из них положат свои значения в очередь, а третий заблокируется навсегда, так как освобождать очередь уже некому.

ага, понятно, спасибо
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408755
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Усовершенствованная версия, без мьютексов.
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
private class LogService {

        private final Thread logThread = new LogThread();
        private final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
        private final AtomicInteger counter = new AtomicInteger();
        private volatile boolean isShutDown = false;

        public void start() {
            logThread.start();
        }

        public void shutdown() {
            isShutDown = true;
        }

        public void log(String msg) throws InterruptedException {
            if(isShutDown || msg == null) {
                throw new IllegalArgumentException();
            }
            counter.incrementAndGet();
            if(isShutDown) {
                counter.decrementAndGet();
                throw new IllegalArgumentException();
            }
            try {
                queue.put(msg);
            } catch(InterruptedException ex) {
                counter.decrementAndGet();
                throw ex;
            }
        }

        private class LogThread extends Thread {
            @Override
            public void run() {
                while(!isShutDown || counter.get() != 0) {
                    try {
                        String elem = queue.poll(1000, TimeUnit.NANOSECONDS);
                        while (elem != null) {
                            try {
                                doWork(elem);
                            } catch(Exception ex) {
                                //task related exception
                            }
                            counter.decrementAndGet();
                            elem = queue.poll();
                        }
                    } catch(InterruptedException ex) {} //never happens
                }
            }

            private void doWork(String msg) {
                System.out.println(msg);
            }
        }

    }
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39408964
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
no56892,
Код: java
1.
   while(!isShutDown || counter.get() != 0) {



получается, что мы так и без остановки явной можем остановиться. Стоит только в какой-то момент очереди опустеть
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39409279
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
|| и && не путаешь?
...
Рейтинг: 0 / 0
LogWrite пример из Concurrency in practice. Где тут race condition?
    #39409527
questioner
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
no56892,

путаешь
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / LogWrite пример из Concurrency in practice. Где тут race condition?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]