请问你能帮帮我吗?
我要做的是打印机示例。将有更多的打印机和更多的文件等待打印。
Printer
是Thread
,它会一直睡到文档到达,然后打印出来,然后再次进入睡眠状态。
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();
}
}
答案 0 :(得分:1)
我想知道1000次运行多少次打印机将打印文件(你会进行实验吗?)。
但是首先将stick.wait()
放在Printer的run方法中,或者你将在那里获得IllegalMonitorStateException异常。
您遇到了称为竞争条件和死锁的并发问题。这意味着结果取决于执行代码的顺序。有时你可能很幸运,程序会像你期望的那样工作。但在其他时候,你将陷入僵局(这是另一个并发编程问题)。
想象一下按顺序执行指令:
PrinterManager.run()
获取信号量
PrinterManager.run()
将文档设置到打印机
PrinterManager.run()
在棒上执行通知
Printer.run()
执行stick.wait()
......这就是你陷入僵局的方式。
在PrinterManager
开始等待之前,stick
已通知Printer
。然后PrinterManager
获取了信号量中的最后一个许可,并开始等到Printer
释放一些(它永远不会,它等待!)。两个对象都开始互相等待,两者都被阻止了。
但是Printer
可能会在PrinterManager
通知对象之前开始等待,而不是按预期工作。这就是为什么你有竞争条件,你永远无法预测会发生什么,这是非常糟糕的。
作为一个简单的解决方案(不是最好的,但很快,没有您可能会发现的其他技术的帮助)您可能会摆脱困境。改为引入两个计数器:
numberOfFinishedPrintTasks
(每次转弯后让Printer
增加)
和lastSubmittedPrintTaskNumber
(make PrinterManager
在每次提交后将其设置为numberOfFinishedPrintTasks + 1
。
PrinterManager
必须检查条件numberOfFinishedPrintTasks == lastSubmittedPrintTaskNumber
这意味着打印机完成了所有工作,并且可以提交新任务。
P.S。不要为Thread
课程继承。您没有向Thread
添加任何新功能,只需让它运行一项特定的工作即可。
为此,您应该创建一个实现Runnable
接口的对象,并将该对象传递给新的Thread
实例。