在两个线程程序

时间:2015-11-13 18:40:18

标签: java multithreading printing synchronization monitor

假设我有以下代码,其中一个线程生成正方形并将它们写入缓冲区,而另一个线程打印它们:

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

}

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

现在,问题。我总是打印从1到68的所有正方形,这是该程序的预期输出。 然而,在输出所有数字后非常罕见的情况下,我遇到了僵局。怎么了?好吧,也许在100个程序执行中有1个。我不得不像疯了一样在NetBeans上点击“F6”来解决僵局。是的,我知道我可以测试这个简单地将所有主要代码放入for循环中。 相反,如果我在Printers的run方法中注释掉打印行,几乎所有时间都会发生死锁。在这里:

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

我不指望这种行为,因为元素仍然从缓冲区获取,生成器应该被唤醒。

为什么会这样?我该如何解决? 对不起,如果问题不够清楚,我会尽力澄清它,如果需要的话。

1 个答案:

答案 0 :(得分:0)

我想我解决了这个问题。这是我得到的:有一个非常短暂的时刻,Generator线程仍然存在(即Thread.isAlive()将返回true),但Generator有将for - 循环留在run()内。如果Printer此时在其while内查询其run()条件,则会尝试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之后,但在设置此公共标志之前