屏障(例如CyclicBarrier)在传递的线程数量小于屏障限制时会导致死锁吗?

时间:2018-07-12 15:25:50

标签: java multithreading concurrency barrier

运行以下代码时,两个启动线程将被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个开始的 thread 正在等待三分之一的人解决此 barrier 。但是,根据CyclicBarrier的Java API文档,CyclicBarrier

  

一种同步辅助工具,它允许一组线程互相等待以到达一个公共的障碍点

我对他们“彼此等待”

感到困惑

问题:“彼此等待”是否意味着循环等待?如果是这样,怎么办?严格来说,这是一个僵局吗?

3 个答案:

答案 0 :(得分:1)

您可以将CyclicBarrier看作根本就不了解线程。这样想吧:

  1. 障碍物保持对await()的通话记录。
  2. 调用await()时,代码会阻塞(该方法不会返回),但是屏障会增加其计数。
  3. 当计数达到构造上给出的parties值时,将重置计数,并释放在调用await()时阻塞的所有线程(即方法返回)。

因此,根据您的情况,在发生第三次调用之前,不会返回对await()的调用,因此您现有的2个线程实际上被卡住了。从技术上讲,这不是死锁,因为它可以很容易地解决(通过再次调用await())。

之所以将其称为 cyclic 是因为,一旦重置提示并释放线程,就可以再次使用它。通常的用法是将parties设置为要在其上同步的线程数,并且这些线程都进入某种循环,从而使用屏障来确保没有线程继续前进到下一个线程直到所有其他线程也都完成了当前迭代为止。

答案 1 :(得分:1)

关于将情况视为死锁所需的循环等待条件,Wikipedia says

  

每个进程必须等待资源被持有   另一个过程,而该过程正在等待第一个过程   释放资源。通常,有一系列等待过程,   P = {P1,P2,…,PN},这样P1正在等待资源持有者   P2,P2正在等待P3拥有的资源,依此类推,直到PN为   等待P1拥有的资源。

您有一组过程P1和P2。他们正在等待某事,但是他们没有等待P3,因为不存在这样的过程。因此,这并不是死锁。

它也不满足以下条件:

  

保留并等待或保留资源当前正在保留一个进程   至少一种资源,并请求正在使用的其他资源   由其他进程持有。

(重点是我的)。您没有任何进程拥有任何资源,因为不存在第三个进程。

答案 2 :(得分:0)

从技术上讲,这不是死锁,因为已经处于屏障状态的两个线程不会相互阻塞,它们正在等待永远不会到达的第三个线程。

但是最终结果与死锁非常相似,一开始可能会令人困惑。

措辞也有些混乱,因为从技术上说,在n极限的循环障碍中,第一个n-1个线程正在等待nth一个线程。

但是,死锁与死锁之间的主要区别在于如何解决它们:线程过多的循环障碍将由更多线程到达来解决,在死锁中,唯一的“解决方案”是杀死已经存在的一个线程等待中。