2011-12-28 5 views
8

Diciamo che ho 1000 file da leggere e, a causa di alcuni limiti, voglio leggere massimo 5 file in parallelo. E, non appena uno di loro è finito, voglio che ne inizi uno nuovo.Come rendere un limite di thread in Java

Ho una funzione principale che ha la lista dei file e provo a cambiare un contatore ogni volta che un thread è finito. ma non funziona!

Qualche suggerimento?

che segue è la funzione di ciclo principale

for (final File filename : folder.listFiles()) { 

Object lock1 = new Object(); 
new myThread(filename, lock1).start(); 
counter++; 
while (counter > 5); 
} 

risposta

19

Le filature generatrici come questa non sono la soluzione giusta. Usa uno ExecutorService e specifica il pool per essere 5. Metti tutti i file in qualcosa come un BlockingQueue o un'altra raccolta thread-safe e tutti quelli in esecuzione possono solo poll() a volontà.

public class ThreadReader { 

    public static void main(String[] args) { 
     File f = null;//folder 
     final BlockingQueue<File> queue = new ArrayBlockingQueue<File>(1000); 
     for(File kid : f.listFiles()){ 
      queue.add(kid); 
     } 

     ExecutorService pool = Executors.newFixedThreadPool(5); 

     for(int i = 1; i <= 5; i++){ 
      Runnable r = new Runnable(){ 
       public void run() { 
        File workFile = null; 
        while((workFile = queue.poll()) != null){ 
         //work on the file. 
        } 
       } 
      }; 
      pool.execute(r); 
     } 
    } 
} 
+0

+1. Non ha senso reinventare la ruota. Penso che tu intenda ThreadPoolExecutor, però. Non c'è ExecutorPool in J2SE che io conosca. –

+0

Jdk5 in poi ci sono molte classi inbuit per la gestione dei thread.Come suggerito da Kylar, è meglio usare Executorpool – kosa

+0

Sì, intendevo in realtà ExecutorService. Risolto e aggiunto un codice sorgente GREY SUPER per darti il ​​succo. – Kylar

0

Qualunque sia il metodo utilizzato per creare un nuovo thread, incrementare un contatore globale, aggiungere un'istruzione condizionale intorno alla creazione filo che se il limite è stato raggiunto, allora non creare un nuovo thread, magari spingere i file su una coda (un elenco?) e quindi è possibile aggiungere un'altra istruzione condizionale, dopo aver creato un thread, se ci sono elementi nella coda, per elaborare prima quegli elementi.

3

L'approccio nella risposta di Kylar è corretto. Utilizzare le classi executor fornite dalle librerie di classi Java piuttosto che implementare il thread pooling da zero (male).


Ma ho pensato che potrebbe essere utile discutere il codice nella domanda e perché non funziona. (Ho compilato alcune delle parti che hai lasciato fuori nel miglior modo possibile ...)

public class MyThread extends Thread { 

    private static int counter; 

    public MyThread(String fileName, Object lock) { 
     // Save parameters in instance variables 
    } 

    public void run() { 
     // Do stuff with instance variables 
     counter--; 
    } 

    public static void main(String[] args) { 
     // ... 
     for (final File filename : folder.listFiles()) { 
      Object lock1 = new Object(); 
      new MyThread(filename, lock1).start(); 
      counter++; 
      while (counter > 5); 
     } 
     // ... 
    } 
} 

OK, così che cosa è sbagliato in questo? Perché non funziona?

Bene, il primo problema è che in main si sta leggendo e scrivendo counter senza eseguire alcuna sincronizzazione. Presumo che venga aggiornato anche dai thread di lavoro: il codice non ha senso altrimenti. Ciò significa che ci sono buone probabilità che i thread principali non vedano il risultato degli aggiornamenti fatti dai thread figli. In altre parole, while (counter > 5); potrebbe essere un ciclo infinito. (In realtà, questo è abbastanza probabile. Il compilatore JIT è permesso di generare il codice in cui il counter > 5 mette alla prova semplicemente il valore di counter lasciato in un registro dopo la counter++; precedente dichiarazione.

Il secondo problema è che il ciclo while (counter > 5); è incredibilmente dispendioso di risorse. Stai dicendo alla JVM di eseguire il polling di una variabile ... e questo farà potenzialmente MILIARDI di volte al secondo ... eseguendo un processore (core) completamente fuori. Non dovresti farlo. implementando questo tipo di cose usando le primitive di basso livello, dovresti usare i metodi Java Object.wait() e Object.notify(), ad esempio il thread principale attende e ogni thread di lavoro lo notifica.

2

Yo Puoi usare un ExecutorService come pool di thread E una coda.

ExecutorService pool = Executors.newFixedThreadPool(5); 
File f = new File(args[0]); 

for (final File kid : f.listFiles()) { 
    pool.execute(new Runnable() { 
     @Override 
     public void run() { 
      process(kid); 
     } 
    }); 
} 
pool.shutdown(); 
// wait for them to finish for up to one minute. 
pool.awaitTermination(1, TimeUnit.MINUTES);