Вызывает ли барьер (например, CyclicBarrier) взаимоблокировку, когда количество пропущенных потоков меньше предела барьера?

При выполнении следующего кода 2 начальных потока будут заблокированы объектом CyclicBarrier * и бесконечно ожидают разблокировки третьего потока.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class MainDeadlock {
  public static void main(String[] args) throws InterruptedException {
    final CyclicBarrier c = new CyclicBarrier(3); 
    Runnable r = () -> {
            try {
                c.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Run!");
    };
    new Thread(r).start();
    new Thread(r).start();
}

}

Таким образом, 2 запущенных потока ждут, пока третий третий разрешит этот барьер. Однако, согласно документации API Java для CyclicBarrier, CyclicBarrier это

Средство синхронизации, которое позволяет набору потоков ожидать друг друга, чтобы достичь общей точки барьера.

Меня смущает то, как они "ждут друг друга",

Вопросы: означает ли "ожидание друг друга" циклическое ожидание? Если да, то как? Строго говоря, это тупиковая ситуация?


person Rui    schedule 12.07.2018    source источник
comment
Я действительно предлагаю вам пойти и продолжить чтение о взаимоблокировках. Это не один. Это неправильное использование средств синхронизации, приводящее к тому, что все потоки ожидают бесконечно? да. Это то же самое, что тупик? Нет.   -  person Michael    schedule 12.07.2018
comment
Это сложнее, так как я не знаю реализации CyclicBarrier   -  person Rui    schedule 12.07.2018


Ответы (3)


Вы можете думать, что CyclicBarrier вообще не знает о потоках как таковых. Подумайте об этом так:

  1. Барьер ведет учет обращений к await().
  2. Когда вызывается await(), код блокируется (метод не возвращается), но барьер увеличивает свой счет.
  3. Когда подсчет достигает значения parties, заданного при построении, подсчет сбрасывается, и все потоки, заблокированные при вызове await(), освобождаются (т. е. метод возвращается).

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

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

person user31601    schedule 12.07.2018

Что касается условия циклического ожидания, необходимого для того, чтобы ситуация считалась тупиковой, Википедия говорит:

каждый процесс должен ожидать ресурса, удерживаемого другим процессом, который, в свою очередь, ожидает освобождения ресурса первым процессом. В общем, существует множество ожидающих процессов, P = {P1, P2, …, PN}, таких, что P1 ожидает ресурса, удерживаемого P2, P2 ожидает ресурса, удерживаемого P3, и так далее, пока PN не будет ожидание ресурса, удерживаемого P1.

У вас есть набор процессов P1 и P2. Они ждут чего-то, но не ждут P3, потому что такого процесса не существует. Так что это не тупик по этой причине.

Он также не удовлетворяет следующему условию:

Удержание и ожидание или удержание ресурсов: процесс в настоящее время удерживает как минимум один ресурс и запрашивает дополнительные ресурсы, которые удерживаются другими процессами.

(выделено мной). У вас нет процессов, содержащих какие-либо ресурсы, потому что третьего процесса не существует.

person Michael    schedule 12.07.2018

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

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

Формулировка тоже немного запутанная, потому что технически на циклическом барьере с лимитом n первые n-1 потока ждут nth.

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

person biziclop    schedule 12.07.2018
comment
На самом деле не имеет значения, что конечный результат похож (while (true); имеет аналогичный конечный результат ...), «тупик» имеет очень конкретное определение, и это его не удовлетворяет. - person Michael; 12.07.2018
comment
@Майкл Точно. Это имеет значение только потому, что это один из источников путаницы в вопросе. - person biziclop; 12.07.2018