如何使线程等待某个事件发生

时间:2017-11-15 13:39:28

标签: java multithreading concurrency

请问你能帮帮我吗?

我要做的是打印机示例。将有更多的打印机和更多的文件等待打印。

PrinterThread,它会一直睡到文档到达,然后打印出来,然后再次进入睡眠状态。

PrinterManager也是一个帖子。它从队列中收集文档并发送给免费的Printer。我正在使用Semaphore来寻找免费的打印机。

问题在于等待通知对。打印机应该等到管理员发送文档。然后它等待1秒并“打印”它。作为锁定对象,我使用stick

出于某种原因,它不起作用。文档成功发送到打印机,但打印机未被唤醒。你能帮我吗,为什么?

打印机线程:

public class Printer extends Thread {

    private final Semaphore semaphore;
    private final Object stick;
    private String document;

    public Printer(Semaphore semaphore, Object stick) {
        this.semaphore = semaphore;
        this.stick = stick;
    }

    public void setDocument(String document) {
        this.document = document;
    }

    @Override
    public void run() {
        while (true) {
            try {
                stick.wait();
                Thread.sleep(1000);
                System.out.println("Printing: " + document);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

经理主题:

public class PrinterManager extends Thread {

    private final Printer[] printers = new Printer[1];
    private final Object stick = new Object();
    private final Semaphore semaphore = new Semaphore(printers.length);
    private final DocumentQueue queue;

    public PrinterManager(DocumentQueue queue) {
        this.queue = queue;
        printers[0] = new Printer(semaphore, stick);
    }

    @Override
    public void run() {
        while (true) {
            try {
                semaphore.acquire();
                String toPrint = takeNextDocument();
                printers[0].setDocument(toPrint);
                synchronized (stick) {
                    stick.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private String takeNextDocument() throws InterruptedException {
        return queue.take();
    }
}

1 个答案:

答案 0 :(得分:1)

我想知道1000次运行多少次打印机将打印文件(你会进行实验吗?)。

但是首先将stick.wait()放在Printer的run方法中,或者你将在那里获得IllegalMonitorStateException异常。

您遇到了称为竞争条件和死锁的并发问题。这意味着结果取决于执行代码的顺序。有时你可能很幸运,程序会像你期望的那样工作。但在其他时候,你将陷入僵局(这是另一个并发编程问题)。

想象一下按顺序执行指令:

  1. PrinterManager.run()获取信号量

  2. PrinterManager.run()将文档设置到打印机

  3. PrinterManager.run()在棒上执行通知

  4. Printer.run()执行stick.wait()

  5. ......这就是你陷入僵局的方式。 在PrinterManager开始等待之前,stick已通知Printer。然后PrinterManager获取了信号量中的最后一个许可,并开始等到Printer释放一些(它永远不会,它等待!)。两个对象都开始互相等待,两者都被阻止了。

    但是Printer可能会在PrinterManager通知对象之前开始等待,而不是按预期工作。这就是为什么你有竞争条件,你永远无法预测会发生什么,这是非常糟糕的。

    作为一个简单的解决方案(不是最好的,但很快,没有您可能会发现的其他技术的帮助)您可能会摆脱困境。改为引入两个计数器:

    numberOfFinishedPrintTasks(每次转弯后让Printer增加)

    lastSubmittedPrintTaskNumber(make PrinterManager在每次提交后将其设置为numberOfFinishedPrintTasks + 1

    PrinterManager必须检查条件numberOfFinishedPrintTasks == lastSubmittedPrintTaskNumber这意味着打印机完成了所有工作,并且可以提交新任务。

    P.S。不要为Thread课程继承。您没有向Thread添加任何新功能,只需让它运行一项特定的工作即可。 为此,您应该创建一个实现Runnable接口的对象,并将该对象传递给新的Thread实例。