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

Код: 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
21.02.2017, 03:46
    #39408206
base2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
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
21.02.2017, 03:53
    #39408207
base2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
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
21.02.2017, 10:11
    #39408310
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
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
21.02.2017, 13:25
    #39408459
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
В дополнение ещё по этой теме хотел бы понять почему автор выбрал правильное решение такое:

Код: 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
21.02.2017, 13:56
    #39408476
Сергей Арсеньев
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
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
21.02.2017, 15:02
    #39408534
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
Сергей Арсеньев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
21.02.2017, 15:07
    #39408539
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
хотя понял, видимо требование про порядок - необязательное. просто как вариант дедлока
...
Рейтинг: 0 / 0
21.02.2017, 15:11
    #39408544
Сергей Арсеньев
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
questioner,

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

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

В общем случае у тебя очередь не обязательно из 1000 элементов.
Предположим, что она ограничена двумя и представим ситуацию, когда очередь в данный момент пуста и мы выставляем shutdownRequested в true. LoggerThread успевает это увидеть, проверяет, что очередь пуста и завершается. В то же время, скажем, 3 producer-треда попадают на put через гонку - 2 из них положат свои значения в очередь, а третий заблокируется навсегда, так как освобождать очередь уже некому.
...
Рейтинг: 0 / 0
21.02.2017, 19:20
    #39408727
no56892
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
Код: 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
21.02.2017, 19:40
    #39408736
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
Сергей Арсеньевquestioner,

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

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

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

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

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

ага, понятно, спасибо
...
Рейтинг: 0 / 0
21.02.2017, 20:31
    #39408755
no56892
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
Усовершенствованная версия, без мьютексов.
Код: 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
22.02.2017, 11:16
    #39408964
questioner
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
LogWrite пример из Concurrency in practice. Где тут race condition?
no56892,
Код: java
1.
   while(!isShutDown || counter.get() != 0) {



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

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


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