我不太精通多线程。我试图通过一个生产者线程重复截图,它将BufferedImage对象添加到ConcurrentLinkedQueue和一个消费者线程将BufferedImage对象的poll队列保存到文件中。我可以通过重复轮询(while循环)来使用它们,但是我不知道如何使用notify()和wait()来使用它们。我曾尝试在较小的程序中使用wait()和notify,但在这里无法实现。
我有以下代码:
class StartPeriodicTask implements Runnable {
public synchronized void run() {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e1) {
e1.printStackTrace();
}
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit()
.getScreenSize());
BufferedImage image = robot.createScreenCapture(screenRect);
if(null!=queue.peek()){
try {
System.out.println("Empty queue, so waiting....");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
queue.add(image);
notify();
}
}
}
public class ImageConsumer implements Runnable {
@Override
public synchronized void run() {
while (true) {
BufferedImage bufferedImage = null;
if(null==queue.peek()){
try {
//Empty queue, so waiting....
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
bufferedImage = queue.poll();
notify();
}
File imageFile = getFile();
if (!imageFile.getParentFile().exists()) {
imageFile.getParentFile().mkdirs();
}
try {
ImageIO.write(bufferedImage, extension, imageFile);
//Image saved
catch (IOException e) {
tracer.severe("IOException occurred. Image is not saved to file!");
}
}
}之前,我进行了一次重复的轮询,以检查是否存在BufferedImage对象。现在,我已经将run方法更改为synchronised,并尝试实现wait()和notify()。我做得对吗?请帮帮忙。谢谢。
发布于 2012-01-12 04:12:21
您为作业使用了错误的Queue。ConcurrentLinkedQueue是一个非阻塞队列,这意味着没有生产者和消费者语义。如果您只使用一个阅读器和一个编写器,那么可以看看SynchronousQueue
简单地说,你的代码可以这样重写。
BlockingQueue<?> queue = new SynchrnousQueue<?>();
class StartPeriodicTask implements Runnable {
public void run() {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e1) {
e1.printStackTrace();
}
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit()
.getScreenSize());
BufferedImage image = robot.createScreenCapture(screenRect);
queue.offer(image); //1
}
public class ImageConsumer implements Runnable {
@Override
public void run() {
while (true) {
BufferedImage bufferedImage = queue.poll(); //2
File imageFile = getFile();
if (!imageFile.getParentFile().exists()) {
imageFile.getParentFile().mkdirs();
}
try {
ImageIO.write(bufferedImage, extension, imageFile);
//Image saved
catch (IOException e) {
tracer.severe("IOException occurred. Image is not saved to file!");
}
}真的就是这样。
让我解释一下。在第//1行,产生线程将图像‘放置’到队列中。我引用place是因为SynchrnousQueue没有深度。实际发生的情况是,线程告诉队列“如果有任何线程请求这个队列中的元素,那么将该线程交给它,然后让我继续。如果没有,我将等待另一个线程准备就绪。”
第//2行类似于1,其中使用线程只是等待,直到有线程提供。这对于单读取器和写入器非常有用。
发布于 2012-01-12 04:10:24
第一个问题是在你的生产者中有不必要的等待:
if(null!=queue.peek()){ // You are the producer, you don't care if the queue is empty
try {
System.out.println("Empty queue, so waiting....");
wait(); // This puts you to bed, your waiting and so is your consumer
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
queue.add(image);
notify();
}这就是您需要的全部内容:
queue.add(image);
notify();下一个问题是消费者中不必要的notify。它在这一点上产生了对其处理的控制,我相信这是你想让你的生产者继续工作的一种方式,但当然你的代码永远不会到那一步。所以这就是:
}else{
bufferedImage = queue.poll();
notify();
}
File imageFile = getFile();
if (!imageFile.getParentFile().exists()) {
imageFile.getParentFile().mkdirs();
}
try {
ImageIO.write(bufferedImage, extension, imageFile);
//Image saved
catch (IOException e) {
tracer.severe("IOException occurred. Image is not saved to file!");
}
}应该看起来更像这样:
}else{
bufferedImage = queue.poll();
File imageFile = getFile();
if (!imageFile.getParentFile().exists()) {
imageFile.getParentFile().mkdirs();
}
try {
ImageIO.write(bufferedImage, extension, imageFile);
//Image saved
catch (IOException e) {
tracer.severe("IOException occurred. Image is not saved to file!");
}
}发布于 2012-01-12 04:09:44
一旦java.util.concurrent库出现在JDK1.5中,就不再需要编写自己的等待/通知逻辑。在2012年,如果您正在做自己的等待/通知,那么您的工作太辛苦了,应该强烈地考虑一下经过实践检验的、真正的java.util.concurrent等效项。
话虽如此,我相信轮询是内置java.util.concurrent.ConcurrentLinkedQueue背后的想法。换句话说,只要ConcurrentLinkedQue是!isEmpty(),消费者就坐在他们自己的Thread和.poll()项中。我见过的大多数实现在!isEmpty()测试之间都会有一秒钟的休眠,但我认为这实际上没有必要。另外,请注意Vint家伙对我的回答的评论,.poll()可能会返回null。考虑java.util.AbstractQueue的替代实现,这些实现可能具有更接近于您正在寻找的阻塞行为。
这个人有一个简单的例子:http://www.informit.com/articles/article.aspx?p=1339471&seqNum=4
最后,读一读Goetz的书"Java Concurrency In Practice“。我几乎可以肯定,它有一个配方来取代你自己的等待/通知。
https://stackoverflow.com/questions/8825762
复制相似问题