2013-03-18 15 views
16

My ThreadPoolExecutor non riesce a creare nuovi thread. In effetti ho scritto un po 'hacky LinkedBlockingQueue che accetta qualsiasi attività (cioè non è limitato) ma chiama un gestore aggiuntivo - che nella mia applicazione sputa la traccia di avviso che il pool è dietro - che mi dà informazioni molto esplicite che il TPE si rifiuta di crea nuovi thread anche se la coda contiene migliaia di voci. Il mio costruttore è il seguente:ThreadPoolExecutor con coda illimitata che non crea nuovi thread

private final ExecutorService s3UploadPool = 
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue); 

Perché non si creano nuovi thread?

+0

Correlato a http://stackoverflow.com/questions/19528304/how-to-get-the-threadpoolexecutor-to-infrease-threads-to-max-before-queueing/19528305#19528305 – Gray

risposta

16

Questo Gotcha è coperto in this blog post:

Questa costruzione del pool di thread non si limiterà a funzionare come previsto. Ciò è dovuto alla logica all'interno di ThreadPoolExecutor in cui vengono aggiunti nuovi thread se non è possibile offrire un'attività alla coda. Nel nostro caso, utilizziamo un LinkedBlockingQueue illimitato, in cui possiamo sempre offrire un'attività alla coda. In pratica significa che non supereremo mai le dimensioni del pool principale e fino alla dimensione massima del pool.

Se è anche necessario disaccoppiare il minimo dalle dimensioni massime del pool, sarà necessario eseguire una codifica estesa. Non sono a conoscenza di una soluzione esistente nelle librerie Java o Apache Commons. La soluzione è creare un BlockingQueue accoppiato che sia a conoscenza di TPE e che si impegna a rifiutare un'attività se sa che TPE non ha thread disponibili, quindi riarma manualmente. È trattato in maggior dettaglio nel post collegato. In ultima analisi la costruzione sarà simile:

public static ExecutorService newScalingThreadPool(int min, int max, long keepAliveTime) { 
    ScalingQueue queue = new ScalingQueue(); 
    ThreadPoolExecutor executor = 
     new ScalingThreadPoolExecutor(min, max, keepAliveTime, TimeUnit.MILLISECONDS, queue); 
    executor.setRejectedExecutionHandler(new ForceQueuePolicy()); 
    queue.setThreadPoolExecutor(executor); 
    return executor; 
} 

Tuttavia più semplicemente impostare corePoolSize-maxPoolSize e non preoccuparsi di queste sciocchezze.

+0

note, puoi ancora ottenere un effetto di ridimensionamento limitato consentendo il timeout dei thread di base (è possibile ridimensionare da 0 a max thread). – jtahlborn

+0

Secondo javadocs: Impostando corePoolSize e maximumPoolSize lo stesso, si crea un pool di thread di dimensioni fisse. Quindi se segui la tua ultima frase starai meglio usando un Executors.newFixedThreadPool (poolSize). – darrickc

+0

@darrickc sembra che la tua modifica sia già stata rifiutata, ma sarebbe opportuno includerla nel commento. Non penso sia valido perché nei miei casi d'uso non voglio che i thread scadano. – djechlin

3

Come menzionato da @djechlin, questo fa parte del comportamento (sorprendente di molti) dello ThreadPoolExecutor. Credo di aver trovato una soluzione un po 'elegante aggirare il problema che vi mostro nella mia risposta qui:

How to get the ThreadPoolExecutor to increase threads to max before queueing?

Fondamentalmente si estendono LinkedBlockingQueue di averlo restituisce sempre false per queue.offer(...) che aggiungerà un thread aggiuntivi al pool, se necessario. Se il pool è già al massimo thread e sono tutti occupati, verrà chiamato lo RejectedExecutionHandler. È il gestore che quindi fa il put(...) nella coda.

Vedere il mio codice lì.

+1

Grazie. Fisso. @kevinarpe. – Gray

3

Esiste una soluzione alternativa a questo problema. Si consideri il seguente implementazione:

int corePoolSize = 40; 
int maximumPoolSize = 40; 
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 
    60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 
threadPoolExecutor.allowCoreThreadTimeOut(true); 

Impostando il allowCoreThreadTimeOut() di true, i fili nel pool possono terminare dopo il timeout specificato (60 secondi in questo esempio). Con questa soluzione, è l'argomento del costruttore corePoolSize che determina la dimensione massima del pool in pratica, poiché il pool di thread crescerà fino a corePoolSize e quindi inizierà ad aggiungere lavori alla coda. È probabile che il pool non possa mai diventare più grande di così, perché il pool non genera nuovi thread fino a quando la coda è piena (il che, dato che lo LinkedBlockingQueue ha una capacità Integer.MAX_VALUE non può mai accadere).Di conseguenza, non ha molto senso impostare maximumPoolSize su un valore superiore a corePoolSize.

Considerazione: il pool di thread ha 0 thread inattivi dopo che è scaduto il timeout, il che significa che ci sarà un po 'di latenza prima della creazione dei thread (normalmente, si avranno sempre thread corePoolSize disponibili).

Ulteriori dettagli sono disponibili nel JavaDoc di ThreadPoolExecutor.

Problemi correlati