6

Sto cercando una guida per un problema logicamente equivalente alla seguente:Come eseguire la valutazione di cortocircuito in Java su due thread paralleli che restituiscono valori booleani?

public boolean parallelOR() { 
    ExecutorService executor = Executors.newFixedThreadPool(2); 
    Future<Boolean> taskA = executor.submit(new SlowTaskA()); 
    Future<Boolean> taskB = executor.submit(new SlowTaskB()); 

    return taskA.get() || taskB.get(); // This is not what I want 
    // Exception handling omitted for clarity 
} 

La costruzione di cui sopra dà il risultato corretto ma aspetta sempre Taska per completare anche se il risultato è già noto dal TaskB ha completato.

Esiste una struttura migliore che consenta di restituire un valore se uno dei thread restituisce true senza attendere il completamento del secondo thread?

(La piattaforma interessata è Android, se questo influenza il risultato).

+0

Potete modificare i compiti? – Collin

+0

Quindi non vuoi aspettare i thread A e B per completare prima di ottenere il valus ??? e lo vuoi quando A è fatto, ottieni il valore e viceversa? –

+0

L'idea sembra essere che se l'attivitàB termina per prima, ed è vera, allora si può creare un cortocircuito e non attendere che l'attività A termini. – Collin

risposta

2

Ecco un'implementazione di ParallelOr usando ExecutorCompletionService. Aspetta le attività finché non si restituisce true. Se nessuno lo fa, alla fine restituisce false.

public class ParallelOr { 

    static class LongTask implements Callable<Boolean> { 

     private int milliseconds; 
     private boolean val; 

     public LongTask(int milliseconds, boolean val) { 
      this.milliseconds = milliseconds; 
      this.val = val; 
     } 

     @Override 
     public Boolean call() throws Exception { 
      try { 
       Thread.sleep(milliseconds); 
      } catch(Exception ex) {} 
      return val; 
     } 
    } 

    static boolean ParallelOr(List<Callable<Boolean>> tasks) { 
     ExecutorService pool = Executors.newFixedThreadPool(tasks.size()); 
     ExecutorCompletionService<Boolean> completionService 
       = new ExecutorCompletionService<Boolean>(pool); 

     for(Callable<Boolean> task : tasks) { 
      completionService.submit(task); 
     } 

     for(int i = 0; i < tasks.size(); i++) { 
      try { 
       Future<Boolean> result = completionService.take(); 
       if(result.get()) { 
        return true; 
       } 
      } catch (InterruptedException e) { 
      } catch (ExecutionException e) {} 
     } 

     return false; 
    } 


    public static void main(String[] args) { 
     ArrayList<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(); 

     tasks.add(new LongTask(1000, true)); 
     tasks.add(new LongTask(500, false)); 
     tasks.add(new LongTask(6000, false)); 

     boolean result = ParallelOr(tasks); 

     System.out.println(result); 
    } 
} 

Grazie a @Lav per aver ricordato la classe ExecutorCompleteionService.

3

provare ad utilizzare ExecutorCompletionService ... qualcosa come

ExecutorService pool = Executors.newFixedThreadPool(2); 
    ExecutorCompletionService<Result> completionService = new ExecutorCompletionService<Result>(pool); 
completionService.submit(new SlowTaskA()); 
completionService.submit(new SlowTaskB()); 
    Future<Result> future; 
      try { 
       future = completionService.take(); 
       Result currentResult=null; 
       try { 
        currentResult = future.get(); 
       } catch (ExecutionException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       // got the 1st result in obj currentResult, return true or obj 
       return true; 
      } catch (InterruptedException e1) { 
       e1.printStackTrace(); 
      } 
+0

+1 - Fantastico! Non ho mai incontrato 'ExecutorCompletionService' prima. –

1

Penso che la logica del monitor potrebbe funzionare bene in questo caso, sebbene dipenda dal fatto che si è in grado di aggiungere modificare le callebles per ricevere un riferimento. Potrebbe assomigliare a questo nel metodo parallelOR():

ExecutorService executor = Executors.newFixedThreadPool(2); 
    final Object monitor = new Object(); 
    Future<Boolean> taskA = executor.submit(new SlowTaskA(monitor)); 
    Future<Boolean> taskB = executor.submit(new SlowTaskB(monitor)); 
    Boolean ret = null,; 
    try { 
     loop:while(true){ 
      synchronized(monitor){ 
       monitor.wait(); 
      } 
      if(taskA.isDone()){ 
       ret = taskA.get(); 
       if(ret.booleanValue()){ 
        taskB.cancel(true); // If you can. 
        break loop; 
       } 
      } 
      if(taskB.isDone()){ 
       ret = taskB.get(); 
       if(ret.booleanValue()){ 
        taskA.cancel(true); 
        break loop; 
       } 
      } 
      // Ifs in case of spurious wake-up 
     }   
    } catch (InterruptedException | ExecutionException e) { 
     e.printStackTrace(); 
    } 

Mentre alla fine del metodo call() nei tuoi callable si avrebbe:

synchronized(monitor){ 
      monitor.notify(); 
     } 
+0

Forse questo è inerente al modo in cui è impostato, ma come ti assicureresti che isDone() restituisca 'true' se stai segnalando dall'interno dell'attività? – Collin

+0

Sono un po 'incerto su cosa intendi qui. Metterei questo blocco alla fine del tuo metodo call() in modo tale che tu sappia esplicitamente che il callable ha terminato l'elaborazione. – SeanTheStudent