ConcurrentLinkedQueue con wait () e notify ()
-
27-10-2019 - |
Domanda
io non sono ben versato in Multi-Threading. Sto cercando di prendere screenshot più volte da un thread produttore, che aggiunge l'oggetto BufferedImage
per ConcurrentLinkedQueue
e un filetto dei consumatori sarà poll
coda per oggetto BufferedImage
per salvarle in un file. Potrei consumare da ripetute polling (ciclo while), ma non so come consumare utilizzando notify()
e wait()
. Ho provato con wait()
e notify
in programmi più piccoli, ma non sono riuscito a realizzare qui.
Ho il seguente codice:
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!");
}
}
}
In precedenza ho avuto un polling ripetuta per verificare l'esistenza di BufferedImage
oggetto. Ora ho cambiato metodo run
come synchronised
e cercato di attuare wait()
e notify()
. Sto facendo corretta? Per favore aiuto. Grazie.
Soluzione
Si utilizza la Queue
sbagliato per il lavoro. Il ConcurrentLinkedQueue
è una coda non bloccante che significa che non ci sono produttori semantica consumatori. Se si sta solo facendo un lettore e uno scrittore di dare un'occhiata a SynchronousQueue
In poche parole il codice può essere riscritto come tale
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!");
}
}
Questo è davvero.
Mi spiego. Alla linea // 1 il filo produzione sarà 'luogo' l'immagine sulla coda. Mi cita luogo perché una SynchrnousQueue non ha profondità. Quello che accade è il filo racconta la coda "Se ci sono le discussioni che chiedono un elemento da questa coda poi dare il quel filo e lasciatemi continuare. Se io non aspetterò fino a quando un altro thread è pronto"
Linea // 2 è simile a 1 in cui il filo che consumano appena attende che un thread sta offrendo. Questa grande opera con un unico lettore single-scrittore
Altri suggerimenti
Il primo problema è l'attesa inutile che avete nel vostro produttore:
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();
}
Questo è tutto ciò che dovrebbe essere necessario:
queue.add(image);
notify();
Il problema successivo è il notify
inutile nel vostro consumatore. Si cede il controllo della sua elaborazione, a quel punto, che credo si vuole essere un modo per ottenere il vostro produttore di andare, ma ovviamente il codice non viene mai a quel punto. Quindi questo:
}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!");
}
}
Dovrebbe guardare più in questo modo:
}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!");
}
}
Once the java.util.concurrent
library came into the JDK1.5, the need to write your own wait/notify logic went right out the door. In 2012, if you are doing your own wait/notify, you are working too hard and should strongly consider the tried and true java.util.concurrent equivalents.
That being said, I believe polling is the idea behind the built in java.util.concurrent.ConcurrentLinkedQueue
. In other words, the consumers sit in their own Thread and .poll() items from the ConcurrentLinkedQue as long as it is !isEmpty()
. Most implementations that I've seen throw some sort of a one second sleep between tests of the !isEmpty()
, but I don't think that is actually necessary. Also, pay note to the Vint guy's comment on my answer, .poll()
may return null
. Consider alternative implementations of java.util.AbstractQueue
that may have blocking behavior closer to what you are looking for.
This guy's got a simple example: http://www.informit.com/articles/article.aspx?p=1339471&seqNum=4
Finally, get the Goetz book "Java Concurrency In Practice" and read it. I'm almost sure it has a recipe for what to use to replace your own home-grown wait/notifys.