Java 问:;答:复杂的僵局评估测试和答案的评分

Java 问:;答:复杂的僵局评估测试和答案的评分,java,multithreading,Java,Multithreading,以下代码是为测试而构造的。在这个测试中,要求读者解释为什么代码在启动代码后不到一秒钟就会进入死锁 有人能准确地描述一下是什么导致了这段代码中的死锁吗 public class Test { static class FailerThread implements Runnable { final Object[] objects; final Random random; final int number; public FailerThread(fin

以下代码是为测试而构造的。在这个测试中,要求读者解释为什么代码在启动代码后不到一秒钟就会进入死锁

有人能准确地描述一下是什么导致了这段代码中的死锁吗

public class Test {

  static class FailerThread implements Runnable {

    final Object[] objects;
    final Random random;
    final int number;

    public FailerThread(final Object[] objects, final int number) {
      this.objects = objects;
      this.random = new Random();
      this.number = number;
    }

    @Override
    public void run() {
      final boolean isWriter = number % 2 == 0;
      int index = random.nextInt(objects.length);
      try {
        while (Thread.interrupted() == false) {
          synchronized (objects) {
            if (isWriter) {
              while (objects[index] == null) {
                System.out.println(number + ": Index " + index + " is null, waiting...");
                objects.wait();
              }
              for (int copyIndex = 0; copyIndex < objects.length; ++copyIndex) {
                if (objects[copyIndex] == null) {
                  objects[copyIndex] = this.objects[index];
                }
              }
              objects.notifyAll();
            } else {
              objects[index] = null;
            }
          }

          ++index;
          if (index >= objects.length) {
            index = 0;
          }
        }
      } catch (InterruptedException e) {
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    final Object[] objects = new Object[10];
    for (int i = 0; i < objects.length; ++i) {
      objects[i] = new Object();
    }

    final int NUM_THREADS = 32;
    final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
    for (int i = 0; i < NUM_THREADS; ++i) {
      executor.execute(new FailerThread(objects, i));
    }
  }
}
公共类测试{
静态类FailerThread实现可运行{
最终目标[]目标;
最终随机;
最终整数;
public FailerThread(最终对象[]对象,最终整数){
this.objects=对象;
this.random=新的random();
这个数字=数字;
}
@凌驾
公开募捐{
最终布尔值isWriter=number%2==0;
int index=random.nextInt(objects.length);
试一试{
while(Thread.interrupted()==false){
已同步(对象){
if(isWriter){
while(对象[索引]==null){
System.out.println(number+“:Index“+Index+”为空,正在等待…”);
对象。wait();
}
对于(int-copyIndex=0;copyIndex=objects.length){
指数=0;
}
}
}捕捉(中断异常e){
}
}
}
公共静态void main(字符串[]args)引发InterruptedException{
最终对象[]对象=新对象[10];
对于(int i=0;i
编辑:此测试的官方答案(与都铎所写内容类似,只是更详细)

上述构造死锁,因为在某一点上,所有“编写器”都等待空值,但由于这些编写器是唯一可以释放它们的编写器,它们将无限期地挂起。然而,更重要的问题是:为什么

乍一看,代码似乎是由那些作者主导的。每个循环都选择一个线程(写入器或空值器)在数组上进行处理,但是当空值器只写入一个空值时,写入器会消除数组中的所有空值。因此,我们可以预期死锁——虽然可能——是不太可能的(但令人惊讶的是,代码在一秒钟内就死锁了)。然而,仔细看,这个假设是错误的,因为我们正在处理线程

给定足够的执行时间,在多线程应用程序中,重要的是:代码的哪一部分实际上能够阻塞?让我们来看看作者/Nulels:

可能出现的最坏情况。
  • 空值器可以在最坏的情况下执行而不产生任何效果。即:它将null写入数组中已为null的位置

  • 在最坏的情况下,编写器可以无限期地阻止

此外,在同步块的开始处,选择(或多或少)随机候选者进入。开始时,写入程序和空写程序都是50%,但是对于每个被阻止的写入程序,机会偏向空写程序的方向。即使一次成功的写操作消除了所有的空值,空值的几率也总是50%或更多,因为编写者的机会(由于阻塞)不断减少。因此,从线程的角度来看,空值器实际上是主要部分,因为整个系统的设计都倾向于将它们作为同步块的候选对象

此外,这是重要的一部分,线程的执行顺序未定义。一个天真的印象是允许哪个线程执行交替,但事实并非如此。同步块没有首选项,并且哪个线程获得访问权是未定义的(可以说:完全随机,尽管不涉及随机)。因此,在所有16个线程都等待同步的情况下,在20次执行中,线程完全交替的概率,正好等于一行调用20个写入程序或20个null程序的概率。但由于空值器占主导地位(20个写入程序什么都不做),因此在一行中调用20个空值器几乎可以保证将整个数组设置为空值,这将导致任何后续写入程序无限期地阻塞

如果向代码中添加更多日志输出以查看实际选择了哪个线程,那么很快就会看到在一行中调用10个或更多空值器,通常是在前200个循环中。紧接着,系统挂起

为什么问这个问题

我目前正在为专家Java程序员开发一个测试集,在编写了所有代码之后,最终需要对其进行测试。好消息是:它成功了

在您抱怨StackOverflow的使用不当之前:请将此示例视为Q&A。对于多线程体系结构的实际实现,可以从这个示例中学到很多东西。由于这是一个专家级的问题——正如预期的那样——没有多少人能够回答它,甚至不理解它。然而,专家级问题的好处是,你也可以从专家级答案中学到很多东西。这就是为什么我包含了完整详细的答案

如何对候选人进行评分

预见到一些人会认为这个问题对于评估测试来说太难了,并且给出你的测试者的观点,这就是候选人将被评定为:

是的,这个问题太难了,而且没有人会在考试中找到正确答案,是什么
 while (!Thread.interrupted())
} else {        
    objects[index] = null;
    while (objects[index]==null) {
        System.out.println(number + ": Index " + index + " is still null, waiting...");
        objects.wait();
    }
}
while (Thread.interrupted() == false) {
    synchronized (objects) {
        System.out.println("running isWriter=" + isWriter + " thread #" + number);