Visualizzazione dei risultati da 1 a 10 su 10
  1. #1
    Utente di HTML.it L'avatar di mainetz
    Registrato dal
    Oct 2003
    Messaggi
    132

    [JAVA]Utilizzo corretto di una Thread Pool

    Ciao a tutti. Vorrei chiedervi quale sia l'utilizzo corretto di una thread pool.

    Sono a conoscenza di cosa sia una thread pool.

    1) La thread pool serve per eseguire thread che fanno tutti le stesse operazioni("uguali") oppure per eseguire thread che hanno compiti diversi?

    2) E' corretto l'utilizzo sottostante?

    codice:
    private ExecutorService pool; //dichiarazione
    
    private int NUMERO_MAX_THREAD;
    
    NUMERO_MAX_THREAD = 1;
    pool = Executors.newFixedThreadPool(NUMERO_MAX_THREAD);
    
    for(int i = 0; i < NUMERO_MAX_THREAD; i++)
    	pool.execute(new JarAnalyzerThread());
    pool.shutdown();
    Grazie mille!

    mainetz.

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284

    Re: [JAVA]Utilizzo corretto di una Thread Pool

    Originariamente inviato da mainetz
    1) La thread pool serve per eseguire thread che fanno tutti le stesse operazioni("uguali") oppure per eseguire thread che hanno compiti diversi?
    Diciamo che "tipicamente" lo si usa per fare più task dello stesso tipo contemporaneamente. In generale ad un Executor vengono passati dei Runnable quindi l'Executor sa solo come lanciare il task ma non cosa faccia il task. Quindi che siano gli stessi tipi di task o diversi tipi di task all'Executor non importa.

    Originariamente inviato da mainetz
    2) E' corretto l'utilizzo sottostante?
    Il linea di massima sì. shutdown() però non è bloccante. Indica solo di iniziare il processo di shutdown in modo "gentile" (non vengono accettati nuovi task ma quelli già inviati verranno completati).
    È abbastanza tipico mettere subito dopo lo shutdown() un awaitTermination() per attendere in modo sincrono il completamento di tutti i task.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Utente di HTML.it L'avatar di mainetz
    Registrato dal
    Oct 2003
    Messaggi
    132
    ok..

    allora non ho capito proprio niente..

    Facciamo un passo alla volta. Una thread pool è un insieme di thread (che facciano la stessa cosa o no non è importante) con una dimensione prestabilita (supponiamo NUMERO_MAX_THREAD = 5).

    Quando viene chiamata:

    codice:
    pool.execute(new JarAnalyzerThread());
    non vengono lanciati 5 thead di tipo JarAnalyzerThread dato che è stata impostata la dimensione massima di pool pari a 5 nella dichiarazione?

    Grazie mille!

    mainetz!

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da mainetz
    Quando viene chiamata:

    codice:
    pool.execute(new JarAnalyzerThread());
    non vengono lanciati 5 thead di tipo JarAnalyzerThread dato che è stata impostata la dimensione massima di pool pari a 5 nella dichiarazione?
    No, con la singola riga sopra, passi all'Executor solamente 1 Runnable, di cui il thread-pool eseguirà il run() nel contesto di un suo thread. Cioè non viene creato un Thread a partire dal tuo Runnable ma viene eseguito il suo run() nel contesto di un thread del thread-pool.

    Tu potresti creare un thread-pool di 5 thread e poi inviargli 10 task. Solo 5 per volta verranno al massimo eseguiti contemporaneamente. Appena un thread ha terminato un task e diventa "idle", ne prende un altro.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  5. #5
    Utente di HTML.it L'avatar di mainetz
    Registrato dal
    Oct 2003
    Messaggi
    132
    Ah ok capito!

    1) Quindi diciamo che ha senso usare una thread pool quando ho molti thread da eseguire e per evitare il problema di eseguirli tutti insieme ne lancio a gruppetti. Giusto??

    Io uso la thread pool perchè devo analizzare dei file in parallelo. I file da analizzare sono elencati in uno stack. Ogni thread fa una pop dello stack (metodo sincronizzato ad hoc che restituisce un elemento finchè ce ne sono, altrimenti ritorna una stringe "end") e poi analizza il file. Sostanzialmente il run è fatto in questa maniera:

    codice:
    public void run(){
    		
    		String[] record = jarAnalyzer.syncPop();
    		while(record[0] != "end"){			
    			//bla bla bla fai quello che devi fare 
    			record = jarAnalyzer.syncPop();
    		}	
    	}
    Il thread ovviamente muore quando la syncPop() ritorna "end".

    E' più corretto lanciare la thread pool così:

    codice:
    while(!this.stack.isEmpty()){
    	pool.execute(new JarAnalyzerThread(this));
    }
    oppure così:

    codice:
    for(int i = 0; i < NUMERO_MAX_THREAD; i++){
    	pool.execute(new JarAnalyzerThread(this));
    }
    Grazie mille!

    mainetz!

  6. #6
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da mainetz
    1) Quindi diciamo che ha senso usare una thread pool quando ho molti thread da eseguire e per evitare il problema di eseguirli tutti insieme ne lancio a gruppetti. Giusto??
    Un thread pool "bounded" (limitato) in cui ci sono al massimo N thread ha tutta una serie di vantaggi.

    Prendiamo il caso di non utilizzo di un thread-pool. Ipotizza un web-server in cui per ogni nuova connessione ricevuta viene semplicemente e banalmente creato un nuovo thread in cui gestire la connessione. In pratica si ha 1 thread per 1 task.
    Finché le richieste in contemporanea non sono elevate può andare bene. Ma che succederebbe se il server fosse molto caricato, sia per puro "caso" sia per volonta "maligna"??

    Creare un numero potenzialmente illimitato di thread può dare dei problemi:

    a) La creazione di ogni nuovo thread richiede un certo tempo e risorse da parte sia del S.O. che della JVM.

    b) Avere moltissimi thread consuma sicuramente molte risorse, principalmente memoria ma anche altre risorse (file, socket, ecc...).

    c) Per via dei motivi sopra detti, un numero elevato di thread può anche compromettere la sicurezza e la stabilità del sistema. Se molti thread consumano molta memoria potrebbe succedere che la JVM esaurisca la memoria, lanciando un OutOfMemoryError. In quel caso ... beh, c'è ben poco da sperare!

    Usando un thread-pool "bounded" (limitato) ci sono al massimo N thread e sopratutto sono già creati. Quando un nuovo task deve essere eseguito, non deve essere creato un nuovo thread (solo se per caso un thread "muore" il thread-pool lo rimpiazza con uno nuovo). Il task viene messo in coda e appena un thread si libera, prende il prossimo task e lo esegue.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  7. #7
    Utente di HTML.it L'avatar di mainetz
    Registrato dal
    Oct 2003
    Messaggi
    132
    Ok grazie mille per la risposta è stata davvero esauriente!!!

    L'ultima domanda riguarda il corretto utilizzo. Sopra ho usato 2 modi per eseguire i thread secondo te quale è il migliore???

    Grazie mille per il prezioso aiuto!

    mainetz!

  8. #8
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da mainetz
    L'ultima domanda riguarda il corretto utilizzo. Sopra ho usato 2 modi per eseguire i thread secondo te quale è il migliore???
    Secondo me non va molto bene che sia il task ad estrarre dall'elenco (stack o lista che sia) il nome del file da trattare. Primo ci sono questioni di sincronizzazione, perché più thread devono accedere all'elenco. Ma poi se l'elenco è vuoto??

    Io sfrutterei Callable/Future. Callable è più o meno simile ad un Runnable ma in più è in grado di restituire un valore e anche lanciare eccezioni. Quando invio un Callable ad un ExecutorService, mi ritorna un Future che rappresenta il "risultato" di una elaborazione asincrona.

    Farei una classe, che implementa Callable, che riceve il nome del jar. Nel suo call() elabora il jar e poi ritorna un risultato.

    Ecco un codice di esempio che ho scritto:

    codice:
    import java.io.*;
    import java.util.*;
    import java.util.concurrent.*;
    import java.util.zip.*;
    
    public class JarAnalyserApplication
    {
        public static void main (String[] args)
        {
            analyze (Arrays.asList (args));
        }
    
        public static void analyze (List<String> filesList)
        {
            try
            {
                //---- creo la lista dei risultati ----
                List<Future<JarAnalyzerResult>> futures = new ArrayList<Future<JarAnalyzerResult>> ();
    
                //---- creo il thread-pool ----
                ExecutorService executor = Executors.newFixedThreadPool (20);
    
                //---- creo i task e li invio al thread-pool ----
                for (String file : filesList)
                {
                    JarAnalyzerTask task = new JarAnalyzerTask (file);
                    Future<JarAnalyzerResult> f = executor.submit (task);
                    futures.add (f);
                }
    
                //---- attendo la fine della elaborazione ----
                executor.shutdown ();
                executor.awaitTermination (Long.MAX_VALUE, TimeUnit.SECONDS);
    
                //---- prelevo i risultati di ogni task ----
                for (Future<JarAnalyzerResult> f : futures)
                {
                    JarAnalyzerResult result = f.get ();
    
                    System.out.println ("jar file: " + result.getFilename ());
    
                    Set<String> packagesSet = result.getPackages ();
    
                    for (String pkg : packagesSet)
                        System.out.println ("   " + pkg);
                }
            }
            catch (ExecutionException ee)
            {
                System.out.println ("Il task ha lanciato la eccezione:");
                System.out.println (ee.getCause ());
            }
            catch (Exception e)
            {
                System.out.println (e);
            }
        }
    }
    
    
    class JarAnalyzerTask implements Callable<JarAnalyzerResult>
    {
        private String jarFilename;
    
        public JarAnalyzerTask (String jarFilename)
        {
            this.jarFilename = jarFilename;
        }
    
        public JarAnalyzerResult call ()
            throws IOException
        {
            ZipFile zipFile = new ZipFile (jarFilename);
    
            Set<String> foldersSet = new HashSet<String> ();
    
            try
            {
                Enumeration<? extends ZipEntry> entriesEnum = zipFile.entries ();
    
                while (entriesEnum.hasMoreElements ())
                {
                    ZipEntry zipEntry = entriesEnum.nextElement ();
    
                    if (!zipEntry.isDirectory ())
                    {
                        String entryName = zipEntry.getName ();
    
                        if (entryName.endsWith (".class"))
                        {
                            File entryFile = new File (entryName);
    
                            foldersSet.add (entryFile.getParent ());
                        }
                    }
                }
            }
            finally
            {
                zipFile.close ();
            }
    
            TreeSet<String> packagesSet = new TreeSet<String> ();
    
            for (String folder : foldersSet)
                packagesSet.add (folder.replace (File.separator, "."));
    
            return new JarAnalyzerResult (jarFilename, packagesSet);
        }
    }
    
    
    class JarAnalyzerResult
    {
        private String jarFilename;
        private Set<String> packagesSet;
    
        public JarAnalyzerResult (String jarFilename, Set<String> packagesSet)
        {
            this.jarFilename = jarFilename;
            this.packagesSet = packagesSet;
        }
    
        public String getFilename () { return jarFilename; }
        public Set<String> getPackages () { return packagesSet; }
    }
    Non è sicuramente perfetto ma può dare l'idea.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  9. #9
    Utente di HTML.it L'avatar di mainetz
    Registrato dal
    Oct 2003
    Messaggi
    132
    Grazie mille è stato utilissimo!!!

    Ciao!

    mainetz!

  10. #10
    Scusa ma non ho capito perchè si mette
    executor.shutdown ();
    executor.awaitTermination (Long.MAX_VALUE, TimeUnit.SECONDS);

    quando appena sotto fai f.get??

    f.get è bloccante cioè il programma si blocca finchè il metodo call non è terminato
    dico giusto?? che serve aspettare che il thread future termini se già f.get poco sotto
    fa già questa parte??

    grazie mille

    Roby

    Originariamente inviato da andbin
    Secondo me non va molto bene che sia il task ad estrarre dall'elenco (stack o lista che sia) il nome del file da trattare. Primo ci sono questioni di sincronizzazione, perché più thread devono accedere all'elenco. Ma poi se l'elenco è vuoto??

    Io sfrutterei Callable/Future. Callable è più o meno simile ad un Runnable ma in più è in grado di restituire un valore e anche lanciare eccezioni. Quando invio un Callable ad un ExecutorService, mi ritorna un Future che rappresenta il "risultato" di una elaborazione asincrona.

    Farei una classe, che implementa Callable, che riceve il nome del jar. Nel suo call() elabora il jar e poi ritorna un risultato.

    Ecco un codice di esempio che ho scritto:

    codice:
    import java.io.*;
    import java.util.*;
    import java.util.concurrent.*;
    import java.util.zip.*;
    
    public class JarAnalyserApplication
    {
        public static void main (String[] args)
        {
            analyze (Arrays.asList (args));
        }
    
        public static void analyze (List<String> filesList)
        {
            try
            {
                //---- creo la lista dei risultati ----
                List<Future<JarAnalyzerResult>> futures = new ArrayList<Future<JarAnalyzerResult>> ();
    
                //---- creo il thread-pool ----
                ExecutorService executor = Executors.newFixedThreadPool (20);
    
                //---- creo i task e li invio al thread-pool ----
                for (String file : filesList)
                {
                    JarAnalyzerTask task = new JarAnalyzerTask (file);
                    Future<JarAnalyzerResult> f = executor.submit (task);
                    futures.add (f);
                }
    
                //---- attendo la fine della elaborazione ----
                executor.shutdown ();
                executor.awaitTermination (Long.MAX_VALUE, TimeUnit.SECONDS);
    
                //---- prelevo i risultati di ogni task ----
                for (Future<JarAnalyzerResult> f : futures)
                {
                    JarAnalyzerResult result = f.get ();
    
                    System.out.println ("jar file: " + result.getFilename ());
    
                    Set<String> packagesSet = result.getPackages ();
    
                    for (String pkg : packagesSet)
                        System.out.println ("   " + pkg);
                }
            }
            catch (ExecutionException ee)
            {
                System.out.println ("Il task ha lanciato la eccezione:");
                System.out.println (ee.getCause ());
            }
            catch (Exception e)
            {
                System.out.println (e);
            }
        }
    }
    
    
    class JarAnalyzerTask implements Callable<JarAnalyzerResult>
    {
        private String jarFilename;
    
        public JarAnalyzerTask (String jarFilename)
        {
            this.jarFilename = jarFilename;
        }
    
        public JarAnalyzerResult call ()
            throws IOException
        {
            ZipFile zipFile = new ZipFile (jarFilename);
    
            Set<String> foldersSet = new HashSet<String> ();
    
            try
            {
                Enumeration<? extends ZipEntry> entriesEnum = zipFile.entries ();
    
                while (entriesEnum.hasMoreElements ())
                {
                    ZipEntry zipEntry = entriesEnum.nextElement ();
    
                    if (!zipEntry.isDirectory ())
                    {
                        String entryName = zipEntry.getName ();
    
                        if (entryName.endsWith (".class"))
                        {
                            File entryFile = new File (entryName);
    
                            foldersSet.add (entryFile.getParent ());
                        }
                    }
                }
            }
            finally
            {
                zipFile.close ();
            }
    
            TreeSet<String> packagesSet = new TreeSet<String> ();
    
            for (String folder : foldersSet)
                packagesSet.add (folder.replace (File.separator, "."));
    
            return new JarAnalyzerResult (jarFilename, packagesSet);
        }
    }
    
    
    class JarAnalyzerResult
    {
        private String jarFilename;
        private Set<String> packagesSet;
    
        public JarAnalyzerResult (String jarFilename, Set<String> packagesSet)
        {
            this.jarFilename = jarFilename;
            this.packagesSet = packagesSet;
        }
    
        public String getFilename () { return jarFilename; }
        public Set<String> getPackages () { return packagesSet; }
    }
    Non è sicuramente perfetto ma può dare l'idea.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.