new Thread (task) .start () VS ThreadPoolExecutor.submit (task) in Android

Nel mio progetto Android ho avuto un sacco di posti in cui ho bisogno di eseguire codice in modo asincrono (una richiesta web, una chiamata a db ecc.). Non si tratta di attività a lungo termine (massimo alcuni secondi). Fino ad ora stavo facendo questo genere di cose con la creazione di un nuovo thread, passandogli un nuovo runnable con l’attività. Ma recentemente ho letto un articolo sui thread e la concorrenza in Java e ho capito che creare una nuova discussione per ogni singola attività non è una buona decisione.

Così ora ho creato un ThreadPoolExecutor nella mia class Application che contiene 5 thread. Ecco il codice:

 public class App extends Application { private ThreadPoolExecutor mPool; @Override public void onCreate() { super.onCreate(); mPool = (ThreadPoolExecutor)Executors.newFixedThreadPool(5); } } 

E inoltre ho un metodo per inviare compiti eseguibili all’esecutore:

 public void submitRunnableTask(Runnable task){ if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){ mPool.submit(task); } else { new Thread(task).start(); } } 

Quindi, quando voglio eseguire un’attività asincrona nel mio codice, ottengo l’istanza di App e richiama il metodo submitRunnableTask passando il runnable ad esso. Come puoi vedere, controllo anche se il pool di thread ha thread liberi per eseguire il mio task, altrimenti, creo un nuovo Thread (non penso che ciò accadrà, ma in ogni caso … I don ‘ Voglio che il mio compito attenda in coda e rallenti l’app).

Nel metodo callback onTerminate dell’applicazione I onTerminate il pool.

Quindi la mia domanda è la seguente: È questo tipo di modello migliore quindi creare nuovi thread nel codice? Quali sono i pro e i contro del mio nuovo approccio? Può causare problemi che non conosco ancora? Puoi consigliarmi qualcosa di meglio per gestire i miei compiti asincroni?

PS Ho una certa esperienza in Android e Java, ma sono ben lungi dall’essere un guru della concorrenza. Quindi potrebbero esserci degli aspetti che non capisco bene in questo tipo di domande. Qualsiasi consiglio sarà apprezzato.

    Questa risposta presuppone che i tuoi compiti siano brevi

    Questo tipo di modello è migliore della creazione di nuovi thread nel codice?

    È meglio, ma è ancora lontano dall’ideale. Stai ancora creando thread per attività brevi . Invece, è sufficiente creare un diverso tipo di pool di thread, ad esempio da Executors.newScheduledThreadPool(int corePoolSize) .

    Qual è la differenza nel comportamento?

    • Un object FixedThreadPool avrà sempre un set di thread da utilizzare e se tutti i thread sono occupati, una nuova attività verrà inserita in una coda.
    • A (predefinito) ScheduledThreadPool , come creato dalla class Executors , ha un pool di thread minimo che mantiene, anche quando è inattivo. Se tutti i thread sono occupati quando arriva una nuova attività, crea un nuovo thread per esso e smaltisce il thread 60 secondi dopo averlo terminato, a meno che non sia di nuovo necessario.

    Il secondo può permettervi di non creare nuovi thread da soli. Questo comportamento può essere ottenuto senza la parte “Programmata”, ma dovrai build tu stesso l’esecutore. Il costruttore è

     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) 

    Le varie opzioni ti consentono di mettere a punto il comportamento.

    Se alcuni compiti sono lunghi …

    E intendo a lungo. Come nella maggior parte della durata della tua applicazione (connessione in tempo reale a 2 vie? Porta del server? Listener multicast?). In tal caso, mettere il Runnable in un esecutore è dannoso – gli executors standard non sono progettati per far fronte a questo, e le loro prestazioni si deteriorano.

    Pensa al tuo pool di thread fisso: se hai 5 task di lunga durata, ogni nuova attività genera un nuovo thread, distruggendo completamente tutti i possibili guadagni del pool. Se si utilizza un esecutore più flessibile, alcuni thread saranno condivisi, ma non sempre.

    La regola generale è

    • Se è un compito breve , usa un esecutore.
    • Se è un compito lungo , assicurati che il tuo esecutore possa gestirlo (ovvero non ha una dimensione massima del pool, o un numero massimo di thread sufficienti per occuparsi di un altro thread per un po ‘di tempo)
    • Se si tratta di un processo parallelo che deve sempre essere eseguito insieme al thread principale, utilizzare un altro thread.

    Per rispondere alla tua domanda – Sì, usare Executor è meglio che creare nuovi thread perché:

    1. Executor fornisce una selezione di diversi pool di thread. Consente il riutilizzo di thread già esistenti che aumenta le prestazioni poiché la creazione di thread è un’operazione costosa.
    2. Nel caso in cui un thread muore, Executor può sostituirlo con un nuovo thread senza influire sull’applicazione.
    3. Le modifiche alle policy multi-threading sono molto più semplici, poiché è necessario modificare solo l’implementazione dell’Executor.

    Sulla base del commento di Ordous ho modificato il mio codice per lavorare con un solo pool.

     public class App extends Application { private ThreadPoolExecutor mPool; @Override public void onCreate() { super.onCreate(); mPool = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 1, TimeUnit.MINUTES, new SynchronousQueue()); } } public void submitRunnableTask(Runnable task){ if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){ mPool.submit(task); } else { new Thread(task).start(); // Actually this should never happen, just in case... } } 

    Quindi, spero che questo possa essere utile a qualcun altro, e se le persone più esperte hanno dei commenti sul mio approccio, apprezzerò molto i loro commenti.