Подтвердить что ты не робот

Документация противоречит ReentrantReadWriteLock. В конечном итоге запись блокировки имеет приоритет над блокировкой чтения или нет в режиме честности?

Из ReentrantLock javadoc:

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

Тема, которая пытается получить честное чтение блокировка (без повторного) блокируется, если удерживается блокировка записи или есть нить писателя ожидания. Нить не получит считывание блокировки до тех пор, пока не будет приобрел и выпустил блокировку записи. Конечно, если ожидающий писатель отказывается от своего ожидания, оставляя один или несколько потоков чтения как самый длинный официантов в очереди с блокировкой записи, тогда эти читатели будут назначить блокировку чтения.

Поток, который пытается получить справедливую блокировку записи (без повторной) будет блокироваться, если оба блокировки чтения и записи не будут свободны (что подразумевает отсутствие ожидающих потоков). (Обратите внимание, что неблокирующий ReentrantReadWriteLock.ReadLock.tryLock() и Методы ReentrantReadWriteLock.WriteLock.tryLock() не соблюдают эту справедливая настройка и приобретет замок, если это возможно, независимо от того, ожидающих потоков.)

Может быть, это проблема с моим английским, но я вижу противоречия на этом декапсии:
Начиная с первого параграфа, я не понимаю смысла приблизительно политики порядка прибытия

  • из первого абзаца я понимаю, что блокировка получает самый старый ожидающий поток. Если самый старый поток - прочитанный поток, то это будет группа прочитанных потоков, которые ждут дольше, чем самый длинный поток записи ожидания.
  • Из второго абзаца я понимаю, что блокировка чтения не будет получена, если существует блокировка записи в wait-set.

Просьба уточнить это противоречие.

4b9b3361

Ответ 1

Здесь, цитируя из вашей цитаты:

или если есть группа потоков чтения

Другими словами: автор выигрывает над единственным читателем; но когда группа читателей хочет замок, те получают блокировку.

Относительно вопроса: "и что действительно означает группа"... это будет деталью реализации, доступной только при просмотре исходного кода.

Ответ 2

Я не вижу никаких противоречий в описании, которое вы цитировали, и я думаю, что вы правильно поняли # 1, но # 2 ошибочно.

И, кстати, я думаю, что описание GhostCat неверно. Ничто не суммирует время ожидания разных потоков и сравнивает их. Логика на самом деле намного проще.

Мой ответ имеет тенденцию быть длинным, но, надеюсь, объяснительным.

Нехороший режим

Сначала начните с блокировки "нечестного" режима. "Несправедливое" означает, что

Несправедливый заблокированный, который постоянно разрешается, может на неопределенный срок отложить один или несколько потоков чтения или записи.

Таким образом, "справедливость" означает, что ни одна нить не может ждать вечно. "Несправедливость" означает, что если существует постоянный поток потоков для получения блокировки чтения, и у нас есть некоторый поток (W1), который ждет блокировки записи, когда приходит новый поток для блокировки чтения (Rn) может быть заблокирован перед потоком W1, и поэтому может произойти в неудачных обстоятельствах на неопределенный срок. Обратите внимание, что даже в "нехорошем" режиме ReentrantReadWriteLock пытается быть достаточно справедливым, это просто не гарантирует справедливости, потому что, как гласит документ, "справедливость" не является бесплатной, а стоимость - более низкой пропускной способностью.

Пример нехорошего режима

Итак, как может произойти несправедливое поведение. Предположим, что существует блокировка записи с записью W0 с потоком, и теперь очередь R0 и R1 ждет блокировки чтения, затем W1 ждет блокировки записи, а также в будущем будет огромный поток новой нити, идущей для блокировки чтения Ri. Также предположим, что поток R1 имеет самый низкий приоритет в системе, и ОС никогда не сталкивается с приоритетом потоков, даже если они не работали очень долго.

  • Блокировка записи удерживается W0, очередь ожидания - [R0, R1, W1]
  • W0 освобождает блокировку записи, R0 разбуждается и получает блокировку чтения, теперь R1 имеет низкий приоритет и не проснулся, поэтому теперь не может получить блокировку чтения. Ожидание очереди теперь [R1, W1]
  • W1 проснулся, но не может получить блокировку из-за R0
  • Теперь, когда R0 по-прежнему удерживает блокировку чтения, приходит новый поток читателя R2. Поскольку считывание блокировки уже получено, и первый поток в очереди ожидания является считывателем R1, R2 немедленно получает блокировку чтения. Блокировка чтения удерживается клавишей [R0, R2]. Ожидание очереди по-прежнему [R1, W1].
  • Теперь R0 освобождает блокировку, но W1 все еще не может получить блокировку записи, поскольку она удерживается теперь R2. Ожидание очереди по-прежнему [R1, W1].
  • Теперь, когда R2 по-прежнему удерживает блокировку чтения, появляется новый поток читателя R3, получает блокировку чтения, и эта же история продолжается и продолжается.

Важно то, что:

  • Первый поток записи W1 заблокирован от чтения потоком чтения R1, который не проснулся, чтобы получить блокировку из-за низкого приоритета и/или чистой неудачи.
  • для вновь прибывшего потока Ri, чтобы выяснить, есть ли какой-либо поток писем во всей очереди, требуется некоторое время и усилия, и, таким образом, применяется более простая эвристика (шаг № 4): будет ли первый поток ожиданий писать или read, а R1 - чтение, позволяющее быстро получить. Также обратите внимание, что эта логика на шаге 4 с проверкой первого потока в очереди - это попытка быть справедливым, о чем я упоминал ранее, и это лучше, чем просто наивная реализация, которая не имеет такой проверки.

Яркий режим

Итак, вернемся к справедливости. Как вы могли бы найти в источники FairSync внутреннего класса (я лишил второстепенных деталей):

class FairSync extends Sync {
     final boolean writerShouldBlock() {
         return hasQueuedPredecessors();
     }
     final boolean readerShouldBlock() {
         return hasQueuedPredecessors();
     }
}

Итак, буквально да, разница между "справедливым" и "не справедливым" заключается в том, что в режиме "справедливого" режима чтения читатель до получения блокировки чтения, которую он мог бы приобрести без нарушения контракта ReadWriteLock, дополнительно проверяет, есть ли другой поток в очереди перед ним. Таким образом, поток W1 из предыдущего примера не мог долго ждать, поскольку R2 и следующий поток не приобрели бы блокировку чтения перед ним.

Пример режима ожидания

Еще одна попытка на том же примере в режиме честности:

  • Блокировка записи удерживается W0, очередь ожидания - [R0, R1, W1]
  • W0 освобождает блокировку записи, R0 получает очередь блокировки чтения [R1, W1]
  • W1 проснулся, но не может получить блокировку из-за R0
  • R2 поступает в очередь. Хотя блокировка чтения удерживается R0 и R2, похоже, также может ее приобрести, но она не делает этого, потому что видит W1 впереди себя. Блокировка чтения удерживается R0, а очередь - [R1, W1, R2]
  • Теперь оба W1 и R2 не могут получить блокировку до тех пор, пока R1 не будет удален из очереди. Из-за этого, наконец, R1 пробуждается, блокирует обработку и освобождает блокировку.
  • Наконец W1 получает блокировку записи и R2, R3, а другие все еще находятся в очереди ожидания.

В терминах этого примера R0 и R1 сформировать "группу", но R2 не принадлежит к этой "группе", поскольку она находится после W1 в очереди.

Резюме

Итак, в первом абзаце описывается, что происходит, когда блокировка освобождается и стратегия проста: первая очередь ожидания получает блокировку. Если первый поток ожидания является read-thread, тогда все остальные прочитанные потоки в очереди до, первый поток записи получает блокировку чтения. Все такие прочитанные потоки называются "группой". Обратите внимание, что это не означает, что все прочитанные потоки ждут блокировки!

Второй абзац описывает, что происходит, когда новый поток чтения приходит и пытается получить блокировку и поведение здесь, фактически соответствует первому абзацу: если в очереди есть очередь ожидания записи перед текущим потоком, он не будет приобретать замок так же, как он не получит блокировку, если он будет добавлен в очередь до того, как будет выпущена блокировка, и будут применяться правила из пункта №1. Надеюсь, это поможет.

Ответ 3

Политика режима честности - это только "приблизительный порядок прибытия", потому что потоки, ожидающие получения блокировки чтения, группируются, а поток, который позже появился для получения блокировки чтения, может получить блокировку раньше, чем другой поток из той же партии пытаясь получить блокировку чтения из-за планирования ОС.

"Группа потоков читателей" может быть только одним потоком.

В спецификации нет противоречия, но это может быть не так ясно, как могло бы быть.

Предположим, что поток A содержит блокировку записи в мьютексе.

Поток B приходит и пытается получить блокировку записи. Затем поток C приходит и пытается получить блокировку чтения. Затем поток D приходит и пытается получить блокировку чтения. Затем поток E приходит и пытается получить блокировку записи. Затем поток F приходит и пытается получить блокировку чтения.

Теперь поток А разблокирует мьютекс. Справедливая политика режима подразумевает, что поток B получит блокировку: он ждал дольше.

Когда поток B освобождает блокировку, потоки C и D получат блокировку чтения, но не поток F. C и D - это "группа потоков считывателей, ожидающих дольше, чем все потоки записи ожидания". Поток F по-прежнему заблокирован, потому что он ждет меньше времени, чем поток E, который является потоком записи.

Если поток E затем отказывается от ожидания (например, время истекает), тогда поток F теперь является самым старым ожидающим потоком, а текущий замок является блокировкой чтения, поэтому поток F может получить блокировку до того, как потоки C и D выпустят их.

Если поток G теперь пытается получить блокировку записи, он будет блокироваться до тех пор, пока все потоки C, D и F не освободят свои блокировки чтения.

Если поток H теперь пытается получить блокировку чтения, он будет блокироваться: есть поток ожидания ожидания.

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

Теперь C, D и F освобождают свои блокировки, поэтому поток G (самый длинный ожидающий поток) получает блокировку записи.

Тема G освобождает блокировку, и поток H получает блокировку: это группа из одного потока считывателя, который ждал дольше, чем любой ожидающий писатель.

Наконец, когда поток H освобождает блокировку, поток я могу ее приобрести.