Domanda

Ho il classico problema di un thread che spinge gli eventi nella coda in arrivo di un secondo thread. Solo che questa volta sono molto interessato alle prestazioni. Quello che voglio ottenere è:

  • Voglio un accesso simultaneo alla coda, al produttore che spinge, al ricevitore che si apre.
  • Quando la coda è vuota, voglio che il consumatore si blocchi sulla coda, in attesa del produttore.

La mia prima idea è stata quella di utilizzare un LinkedBlockingQueue , ma presto mi sono reso conto che non è simultaneo e le prestazioni ne hanno risentito. D'altra parte, ora uso un ConcurrentLinkedQueue , ma sto ancora pagando il costo di wait () / notification () su ogni pubblicazione . Poiché il consumatore, trovando una coda vuota, non si blocca, devo sincronizzare e wait () su un lucchetto. D'altra parte, il produttore deve ottenere quel blocco e notification () su ogni singola pubblicazione. Il risultato complessivo è che sto pagando il costo di sycnhronized (lock) {lock.notify ()} in ogni singola pubblicazione, anche quando non necessario.

Ciò che suppongo sia necessario qui, è una coda che è sia bloccante che concorrente. Immagino che un'operazione push () funzioni come in ConcurrentLinkedQueue , con un ulteriore notification () all'oggetto quando l'elemento push è il primo nella lista. Un tale controllo che considero già esistente nel ConcurrentLinkedQueue , poiché il push richiede la connessione con l'elemento successivo. Pertanto, questo sarebbe molto più veloce della sincronizzazione ogni volta sul blocco esterno.

Qualcosa del genere è disponibile / ragionevole?

È stato utile?

Soluzione

Penso che tu possa attenerti a java.util.concurrent.LinkedBlockingQueue indipendentemente dai tuoi dubbi. È concorrente. Tuttavia, non ho idea delle sue prestazioni. Probabilmente, altre implementazioni di BlockingQueue faranno al caso tuo. Non ce ne sono molti, quindi esegui test e misurazioni delle prestazioni.

Altri suggerimenti

Simile a questa risposta https://stackoverflow.com/a/1212515/1102730 ma un po 'diverso .. Ho finito per usare un ExecutorService . È possibile creare un'istanza utilizzando Executors.newSingleThreadExecutor () . Avevo bisogno di una coda simultanea per leggere / scrivere immagini buffered su file, nonché atomicità con letture e scritture. Ho solo bisogno di un singolo thread perché il file IO è di ordini di grandezza più veloce della sorgente, IO di rete. Inoltre, ero più preoccupato dell'atomicità delle azioni e della correttezza rispetto alle prestazioni, ma questo approccio può essere fatto anche con più thread nel pool per accelerare le cose.

Per ottenere un'immagine (Try-Catch-finally omesso):

Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
    @Override
        public BufferedImage call() throws Exception {
            ImageInputStream is = new FileImageInputStream(file);
            return  ImageIO.read(is);
        }
    })

image = futureImage.get();

Per salvare un'immagine (Try-Catch-finally omesso):

Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
    @Override
    public Boolean call() {
        FileOutputStream os = new FileOutputStream(file); 
        return ImageIO.write(image, getFileFormat(), os);  
    }
});

boolean wasWritten = futureWrite.get();

È importante notare che è necessario svuotare e chiudere i flussi in un blocco finally. Non so come si comporti rispetto ad altre soluzioni, ma è piuttosto versatile.

Suggerirei di guardare ThreadPoolExecutor newSingleThreadExecutor. Gestirà il mantenimento delle attività ordinate per te e se invii Callables al tuo esecutore, sarai in grado di ottenere anche il comportamento di blocco che stai cercando.

Puoi provare LinkedTransferQueue da jsr166: http : //gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

Soddisfa le tue esigenze e ha un sovraccarico minore per le offerte / sondaggi. Come posso vedere dal codice, quando la coda non è vuota, utilizza operazioni atomiche per gli elementi di polling. E quando la coda è vuota, gira per qualche tempo e parcheggia il thread se non ha successo. Penso che possa aiutarti nel tuo caso.

Uso ArrayBlockingQueue ogni volta che devo passare i dati da un thread all'altro. Usando i metodi put e take (che si bloccheranno se pieno / vuoto).

Ecco un elenco di classi che implementano BlockingQueue .

Consiglierei di controllare SynchronousQueue .

Come @Rorick menzionato nel suo commento, credo che tutte queste implementazioni siano simultanee. Penso che le tue preoccupazioni con LinkedBlockingQueue possano essere fuori posto.

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