ConcurrentLinkedQueue with wait () and notify ()

I do not understand multithreading. I am trying to take a screenshot several times with a single producer thread that adds a BufferedImage object to ConcurrentLinkedQueue , and a consumer thread poll queue for the BufferedImage object to save them to a file. I could consume them by re-polling (while loop), but I don't know how to use them using notify() and wait() . I tried using wait() and notify in small programs, but couldn't implement it here.

I have the following code:

 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!"); } } } 

I used to have a second polling to check for the existence of a BufferedImage object. Now I changed the run method as synchronised and tried to implement wait() and notify() . Am I doing the right thing? Please help. Thanks.

+4
source share
3 answers

You are using the wrong Queue for the job. ConcurrentLinkedQueue is a non-blocking queue, which means that consumer semantics do not exist. If you just make one reader and one author take a look at SynchronousQueue

Simply put, your code can be rewritten as such

 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!"); } } 

It really is.

Let me explain. In line // 1, the producing thread will "put" the image in the queue. I quote the place because SynchrnousQueue has no depth. What actually happens is that the thread tells the queue "If there are any threads requesting an item from this queue, then give it that thread and let me continue. If not, I will wait until another thread is ready "

Line // 2 is similar to 1, where the consuming thread just waits until the thread suggests. This works great with a single-reader writer.

+5
source

Once the java.util.concurrent library got into JDK1.5, you had to write your own wait / notification logic to exit it. In 2012, if you do your own wait / notify, you work too hard and have to take into account the proven and true equivalents of java.util.concurrent strongly.

Saying, I think polling is the idea of ​​the built-in java.util.concurrent.ConcurrentLinkedQueue. In other words, consumers sit in their own Thread and .poll () elements from ConcurrentLinkedQue, if any! IsEmpty (). Most of the implementations that I have seen throw one sleep dream between tests! IsEmpty (), but I do not think it is really necessary. Also, take a look at the Vint guy's comment on my answer, .poll () may return null. Consider alternative implementations of java.util.AbstractQueue that may have locking behavior closer to what you are looking for.

This guy got a simple example: http://www.informit.com/articles/article.aspx?p=1339471&seqNum=4

Finally, take Goetz's Java Concurrency In Practice and read it. I am pretty sure that he has a recipe for what to use to replace his own home expectations / notifys.

+4
source

The first problem is the unnecessary wait that your producer has:

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

This is all you need:

  queue.add(image); notify(); 

The next question is unnecessary notify for your consumer. This gives you control over its processing at that moment, which, I believe, you intend to make your producer, but, of course, your code never comes to that. So:

  }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!"); } } 

It should look bigger:

  }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!"); } } 
+4
source

Source: https://habr.com/ru/post/1390519/


All Articles