简体   繁体   English

防止两线程程序中的死锁

[英]Preventing deadlock in two thread program

Suppose I have the following code, where one thread generates squares and writes them to a buffer while another thread prints them: 假设我有以下代码,其中一个线程生成正方形并将其写入缓冲区,而另一个线程打印它们:

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();
}

} }

Generator should generate squares of numbers until it reaches some limit ( limit = 69 , in this case). 生成器应生成数字平方,直到达到一定的限制(在这种情况下, 限制= 69 )。 Printer should print all values generated by Generator. 打印机应打印发生器生成的所有值。 Buffer works somewhat like ring buffer. 缓冲区的工作方式类似于环形缓冲区。 Indexes for putting ( in ) and taking ( out ) element are cycling in bounds of buffer size. 放入( in )和取出( out )元素的索引在缓冲区大小的边界内循环。 Buffer has methods for putting and taking elements from buffer. 缓冲区具有从缓冲区放入和取出元素的方法。 Generator thread cannot put elements in buffer if it is full (that is, there are no zero elements; zero element is 0, for precision sake...). 如果生成器线程已满,则无法将元素放入缓冲区(也就是说,没有零元素;为了精确起见,零元素为0 ...)。 Printer works this way: first it checks if there are any alive generator threads and then checks if buffer contains only zero elements. 打印机以这种方式工作:首先,它检查是否有任何活动的生成器线程,然后检查缓冲区是否仅包含零个元素。 If neither of these conditions is true, printer thread terminates. 如果这些条件都不成立,则打印机线程终止。

Now, to the problem. 现在,解决问题。 I always get printed all squares from 1 to 68, which is expected output of this program. 我总是打印从1到68的所有正方形,这是该程序的预期输出。 However , on very rare occasion after all numbers had been output I get a deadlock. 但是 ,在极少数情况下,在输出所有数字之后,我陷入了僵局。 How rarely? 很少? Well, maybe in 1 out of 100 executions of program. 好吧,也许在每100次执行的程序中就有1次。 I had to keep hitting "F6" on NetBeans like crazy to get a deadlock. 我必须不断疯狂地在NetBeans上按“ F6”,才能陷入僵局。 And yes, I know that I can test this simply putting all main code in for loop. 是的,我知道我可以将所有主要代码放入for循环中进行测试。 Conversely , if I comment out print line in Printers' run method, deadlock happens almost all the time. 相反 ,如果我在Printers的run方法中注释掉打印行,则几乎总是发生死锁。 Here: 这里:

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

I do not expect this behavior, because element still gets taken from buffer and generator should be awoken. 我不希望这种行为,因为元素仍然从缓冲区中获取,并且应该唤醒生成器。

Why does this happen? 为什么会这样? And how do I fix it? 我该如何解决? Sorry if question isn't clear enough, I'll try to clarify it as best I can if needed. 抱歉,如果您对问题的理解不够清楚,我会在需要时尽力澄清。

I think I fount the problem. 我想我发现了这个问题。 Here is what I got: There is a very short moment in time, where the Generator thread is still alive (ie Thread.isAlive() will return true ), but the Generator has left the for -loop within run() . 这就是我得到的结果:在很短的时间内, Generator线程仍处于活动状态(即Thread.isAlive()将返回true ),但是Generator已在run()留下了for循环。 If the Printer queries its while -condition within its run() at this point in time, it will try to take() something, that is not there (and never will be). 如果Printer查询其run()内的while ,它将尝试take()不存在的东西(永远不会出现)。 Indeed, you can verify, that the Generator always finishes, meaning termination detection on the side of the Printer is faulty. 的确,您可以验证Generator始终完成,这意味着Printer侧面的终止检测是错误的。 For a hot fix, you can simply add a magic constant is Printer s while condition: 对于热修复,您可以简单地添加一个神奇常量,即Printer s while条件:

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

For a clean termination detection, you could set some common flag-variable to false , signaling that the Generator has finished work and the Printer can stop working. 为了进行干净的终止检测,您可以将一些公共标志变量设置为false ,以指示Generator已完成工作并且Printer可以停止工作。 But this has to be done in a synchronized manner, meaning the Printer is not allowed to query this condition, while the Generator is after its last push , but before it sets this common flag. 但这必须以同步方式完成,这意味着当Generator在其最后一次push之后但在设置此公共标志之前,不允许Printer查询此条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM