使用wait()和notify()的java控制线程
(问题已解决,解决方案如下)
我有两门课:装备课和指挥课。该设备是一种运行命令的设备,但我需要它能够同时运行1个命令。
命令是一个线程,在run()函数上执行,而Equip是一个普通类,不扩展任何内容。
目前,我有以下设置来运行这些命令:
命令类:
@Override
public void run() {
boolean execute = equip.queueCommand(this);
if (!execute) {
// if this command is the only one on the queue, execute it, or wait.
esperar();
}
// executes the command.....
equip.executeNextCommand();
}
synchronized public void esperar() {
try {
this.wait();
} catch (Exception ex) {
Log.logErro(ex);
}
}
synchronized public void continue() {
this.notifyAll();
}
装备等级:
public boolean queueCommand(Command cmd) {
// commandQueue is a LinkedList
commandQueue.addLast(cmd);
return (commandQueue.size() == 1);
}
public void executeNextCommand() {
if (commandQueue.size() >= 1) {
Command cmd = commandQueue.pollFirst();
cmd.continue();
}
}
然而,这是行不通的。基本上,notify()不会唤醒命令线程,因此它永远不会执行。 我搜索了等待和通知协议,但没有发现代码有任何错误。我还尝试直接从queueCommand()方法调用wait(),但后来queueCommand的执行停止了,它也没有完成它应该做的事情。 这种方法正确吗?我遗漏了什么,还是完全错了,我应该实现一个监控类来操作并发线程
编辑:多亏了@Gray,我用另一种完全不同的方法解决了这个问题,使用了Executors
这是最后一段代码,也许有一天会对某人有所帮助:
装备等级:
private ExecutorCompletionService commandQueue = new ExecutorCompletionService(Executors.newFixedThreadPool(1));
public void executeCommand(Command cmd, boolean waitCompletion) {
commandQueue.submit(cmd, null);
if (waitCompletion) {
try {
commandQueue.take();
} catch (Exception ex) {
}
}
}
在Command类中,我只有一个方法来封装equip的execute方法。 当我同时需要命令的结果时,会使用布尔waitCompletion,而不是调用一个新线程来执行它,我只是执行并等待,假装它在同一个线程上执行。这个问题对这个问题进行了很好的讨论:When would you call java's thread.run() instead of thread.start()?。是的,在这种情况下,打电话是很有用的。运行()而不是。开始()
# 1 楼答案
ExecutorService是一个不错的选择。但是如果你想自己做,或者需要做一些更有趣的事情,我提供以下建议
据我所知,这一切都是由Equip的queueCommand驱动的,它可以在任何时间、任何地点从任何线程调用。首先,Equip中的两个方法应该同步,这样commandQueue就不会被破坏。(您可以使用ConcurrentLinkedQueue,但要小心计数。)更好的是,将每个方法中的代码放在一个由queueCommand同步的块中
但更进一步,我认为你们的两门课结合起来效果更好。将命令切换为简单的可运行命令,我会尝试以下方法:
您需要捕获一些异常,并找出如何启动和关闭它们,但这应该让您了解等待和通知是如何工作的。(我会想办法知道什么时候“run”没有等待,这样我就可以跳过queueCommand中queueLock上的同步,而是在运行之前先走。)
# 2 楼答案
如果从多个线程调用
Command.run()
,代码中存在大量的竞争条件。除非这是一个必须自己实现代码的家庭作业问题,否则我强烈建议使用1.6中添加的JavaExecutors
之一。在本例中,Executors.newSingleThreadExecutor()
是将正在运行的后台任务数限制为1所需的。这将允许无限数量的任务提交给ExecutorService
,但在任何时候都只执行其中一个任务如果您需要在另一个任务已经在运行时将任务提交到block的线程,那么您可以使用以下方法。这将设置一个最多1个线程的池,并使用一个
SynchronousQueue
阻塞,直到工作线程消耗作业:但是如果是这样的话,那么您可以直接在
synchronized
块内调用任务,而不需要ExecutorService
最后,对于任何(任何语言的)新并发程序员,我建议您花时间阅读一些关于这个主题的文档。在你开始意识到线程化过程中固有的并发缺陷之前,即使是最简单的类集合,让你的代码正常工作也是一个令人沮丧的过程Doug Lea's book是《圣经》中关于这个主题的一篇文章。如果我低估了你在这方面的经验,我深表歉意