2015-11-13 136 views
2

假设我有以下代码,其中,一个线程产生平方和,而另一个线程打印它们把它们写到一个缓冲器:两个线程程序防止死锁

import java.util.*; 

public class Something { 

public static Buffer buffer = new Buffer(); 

public static class Buffer { 

    private int[] buffer; 
    private static final int size = 10; 

    //Indexes for putting and taking element form buffer 
    private int in, out; 

    //Number of elements in buffer 
    private int k; 

    public Buffer() { 
     buffer = new int[size]; 
     in = 0; 
     out = 0; 
     k = 0; 
    } 

    public synchronized void put(int e) { 
     try { 
      while (k == buffer.length) { 
       wait(); 
      } 
     } catch (InterruptedException ex) { 
     } 
     buffer[in] = e; 
     k++; 
     in = ++in % size; 
     notifyAll(); 
    } 

    public synchronized int take() { 
     try { 
      while (k == 0) { 
       wait(); 
      } 
     } catch (InterruptedException ex) { 
     } 
     int e = buffer[out]; 
     buffer[out] = 0; 
     out = ++out % size; 
     k--; 
     notifyAll(); 
     return e; 
    } 

    public synchronized boolean notEmpty() { 
     return k != 0; 
    } 

} 

public static class Generator implements Runnable { 

    int limit; 

    public Generator(int lim) { 
     limit= lim; 
    } 

    @Override 
    public void run() { 
     for (int i = 1; i < limit; i++) { 
      buffer.put(i * i); 
     } 
    } 
} 

public static class Printer implements Runnable { 

    private Thread[] generators; 

    public Printer(Thread[] gen) { 
     generators = gen; 
    } 

    public synchronized boolean nobody() { 
     for (Thread th : generators) { 
      if (th.isAlive()) { 
       return false; 
      } 
     } 
     return true; 
    } 

    @Override 
    public void run() { 
     int x = 0; 
     while (!nobody() || buffer.notEmpty()) { 
      x = buffer.take(); 
      System.out.println(x); 
     } 
    } 
} 

public static void main(String[] args) throws InterruptedException { 
    Thread generator = new Thread(new Generator(69)); 
    Thread printer = new Thread(new Printer(new Thread[]{generator})); 

    generator.start(); 
    printer.start(); 

    generator.join(); 
    printer.join(); 
} 

}

发生器应该产生的平方直到达到某个限制(在这种情况下,限制= 69)。打印机应打印由Generator生成的所有值。缓冲区有点像环形缓冲区。用于放置()和取()元素的索引在缓冲区大小的范围内循环。缓冲区有从缓冲区中放入和取出元素的方法。发生器线程如果已满(即,没有零元素;为了精确起见零元素为0),它不能将元素放入缓冲区中。打印机以这种方式工作:首先检查是否有活动的生成器线程,然后检查缓冲区是否只包含零个元素。如果这两个条件都不成立,则打印机线程终止。

现在,解决问题。我总是打印从1到68的所有方块,这是该程序的预期输出。 但是,在所有数字输出后非常罕见的情况下,我得到了一个僵局。多少次?那么,也许在100个程序执行中有1个。我不得不继续在NetBeans上点击“F6”,以免陷入僵局。是的,我知道我可以测试这个只需将所有主代码放入for循环。 相反,如果我在打印机的运行方法中注释掉打印行,几乎总是发生死锁。在这里:

 @Override 
     public void run() { 
     int x = 0; 
     while (!nobody() || buffer.notEmpty()) { 
      x = buffer.take(); 
      //System.out.println(x); 
     } 
    } 

我不希望这种行为,因为元素仍然会从缓冲和发电机所应被唤醒。

为什么会发生这种情况?我该如何解决它? 很抱歉,如果问题不够清楚,我会尽可能地澄清它,如果需要的话。

+0

您可以确定哪个线程卡住(第一个)? – Turing85

+0

我确定它是打印机。它永远在等待Generator将一些元素放入缓冲区。 – Schizo

+1

这个程序看起来非常复杂,它应该更复杂。为什么不使用队列并删除所有同步的块。 – Leon

回答

0

我想我解决了这个问题。这是我得到的结果:有一个很短的时间,其中Generator线程仍然存在(即Thread.isAlive()将返回true),但Generator已将for -loop离开run()。如果Printer在此时间内在其run()内查询其while条件,它将尝试take()东西,那不是(并且永远不会)。事实上,您可以验证Generator总是结束,这意味着Printer一侧的终止检测出现故障。对于一个热修复程序,你可以简单地添加了一个幻常量为Printer s,而条件:

@Override 
public void run() { 
    int x = 0; 
    int count = 0; 
    while (++count < 69) { 
     x = buffer.take(); 
     System.out.println(x); 
    } 
} 

对于一个干净终止检测,你可以一些常见的标志变量设置为false,这表明该Generator已经完成的工作, Printer可以停止工作。但是这必须以同步的方式完成,这意味着Printer不允许查询这个条件,而Generator在它的最后push之后,但是在它设置该公共标志之前。

+1

实际上,nobody()上的同步根本没有用,所以应该删除以避免混淆。 – JimmyB