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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top