2016-02-05 13 views
6

Ho una lista che contiene circa 200K elementi.Passaggio di un elenco Iterator a più thread in Java

Sono in grado di passare l'iteratore per questa lista di più thread e li hanno iterare l'intero lotto, senza che nessuno di loro accedono agli stessi elementi?

Questo è quello che sto pensando in questo momento.

principale:

public static void main(String[] args) 
{ 
    // Imagine this list has the 200,000 elements. 
    ArrayList<Integer> list = new ArrayList<Integer>(); 

    // Get the iterator for the list. 
    Iterator<Integer> i = list.iterator(); 

    // Create MyThread, passing in the iterator for the list. 
    MyThread threadOne = new MyThread(i); 
    MyThread threadTwo = new MyThread(i); 
    MyThread threadThree = new MyThread(i); 

    // Start the threads. 
    threadOne.start(); 
    threadTwo.start(); 
    threadThree.start(); 
} 

MyThread:

public class MyThread extends Thread 
{ 

    Iterator<Integer> i; 

    public MyThread(Iterator<Integer> i) 
    { 
     this.i = i; 
    } 

    public void run() 
    { 
     while (this.i.hasNext()) { 
      Integer num = this.i.next(); 
      // Do something with num here. 
     } 
    } 
} 

mio risultato desiderato è che ogni filo sarebbe elaborare circa 66.000 elementi ciascuno, senza bloccare l'iteratore troppo, e anche senza di i thread che accedono allo stesso elemento.

Questo suono è fattibile?

+1

L'utilizzo di Java 8 'Stream's e' parallel() 'sembra essere il caso d'uso appropriato qui. –

+1

No, non puoi (in sicurezza, con questo codice), perché le chiamate 'hasNext' e' next' non sono atomiche. –

+0

@AndyTurner Con gli stream, l'OP non gestirà manualmente gli iteratori. –

risposta

4

Hai davvero necessario manipolare i thread e gli iteratori manualmente? È possibile utilizzare Java 8 Stream s e lasciare che parallel() esegua il lavoro.

Per impostazione predefinita, verrà utilizzato un filo di meno quando si dispone di processori.

Esempio:

list.stream() 
    .parallel() 
    .forEach(this::doSomething) 
; 

//For example, display the current integer and the current thread number. 
public void doSomething(Integer i) { 
    System.out.println(String.format("%d, %d", i, Thread.currentThread().getId())); 
} 

Risultato:

49748, 13 
49749, 13 
49750, 13 
192710, 14 
105734, 17 
105735, 17 
105736, 17 
[...] 

Edit: se si utilizza Maven, è necessario aggiungere questo pezzo di configurazione pom.xml al fine di utilizzare Java 8:

<build> 
    <plugins> 
    <plugin> 
     <groupId>org.apache.maven.plugins</groupId> 
     <artifactId>maven-compiler-plugin</artifactId> 
     <version>3.3</version> 
     <configuration> 
     <source>1.8</source> 
     <target>1.8</target> 
     </configuration> 
    </plugin> 
    </plugins> 
</build> 
+0

Sembra un'ottima soluzione, ma sto ottenendo "I riferimenti del metodo non sono supportati a questo livello di lingua" quando provo a fare quanto sopra. Qualche idea? –

+0

Il progetto non è configurato per l'utilizzo di Java 8. Suggerimento: se si utilizza Maven, è necessario aggiungere una parte di configurazione. –

+0

@ TomWright Ho aggiunto il pezzo di maven conf per Java 8 –

2

Non si può farlo in un modo thread-safe con un singolo iteratore. Suggerisco di usare sottoliste:

metodo
List sub1 = list.subList(0, 100); 
List sub2 = list.subList(100, 200); 

ArrayList#subList() sarà solo avvolgere la lista data senza copiare elementi. Quindi è possibile scorrere ogni sottocartella in un thread diverso.

1

Dal next() metodo della classe che implementa 012 L'interfacciamanipola i dati, l'uso simultaneo del metodo next() richiede la sincronizzazione. La sincronizzazione può essere realizzato utilizzando blocco sincronizzato su oggetto iteratore come segue:

synchronized(i) 
{ 
    i.next(); 
} 

Però, consigliabile l'utilizzo di Flusso API come nella risposta precedente se il bisogno è solo l'elaborazione parallela della lista.

0

Ciao per evitare che le discussioni che da dreadlocks o la fame è possibile utilizzare l'ExecutorService dalla classe pool di thread. Queste parole sono migliori per me che usare sincronizzati, serrature o serrature per il rientro. Puoi anche provare a usare Fork/Join ma non l'ho mai usato prima. Questo è un esempio di codice, ma spero che l'idea

public static void main(String[] args){ 
    ExecutorService executor = Executors.newFixedThreadPool(200000); 
    List<Future<Integer>> futureList = new ArrayList<>(); 
    //iteration code goes here 
    executor.shutdown(); 
} 

Public class MyThread implements Callable<ArrayList<Integer>>{ 

@Override 
     public Iterator<Integer> call() throws Exception { 
      //code goes here! 
     } 

} 
0

Se si utilizza un flusso parallelo, sarete in esecuzione il codice in molte discussioni, con gli elementi distribuiti in modo uniforme tra i thread:

list.parallelStream().forEach(this::processInteger); 

Questo approccio semplifica notevolmente la codifica; tutto il sollevamento pesante è fatto dal JRE.

Inoltre, per quanto riguarda il codice, è di pessimo stile estendere Thread. Invece, implementare Runnable e passare un'istanza al costruttore di Thread - vedere in diretta

Problemi correlati