6

ich für ein Problem logisch äquivalent zu dem folgenden für die Führung suchen:Wie führt man in Java eine Kurzschlussauswertung auf zwei parallelen Threads durch, die boolesche Werte zurückgeben?

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 
} 

Die obige Konstruktion gibt das richtige Ergebnis aber immer wartet Taska sogar abzuschließen, wenn das Ergebnis bereits seit TaskB hat bekannt abgeschlossen.

Gibt es eine bessere Konstruktion, die es ermöglicht, dass ein Wert zurückgegeben wird, wenn einer der beiden Threads den Wert true zurückgibt, ohne auf den Abschluss des zweiten Threads zu warten?

(Die betroffene Plattform ist Android, wenn das das Ergebnis beeinflusst).

+0

Können Sie die Aufgaben ändern? – Collin

+0

Sie wollen also nicht warten, bis die Threads A und B abgeschlossen sind, bevor Sie den Valus bekommen haben ??? und du willst es, wenn A fertig ist, bekommst du den Wert und umgekehrt? –

+0

Die Idee scheint zu sein, dass wenn taskB zuerst beendet wird und wahr ist, dann kann man kurzschließen und nicht warten, bis TaskA beendet ist. – Collin

Antwort

2

Hier ist eine Implementierung von ParallelOr ExecutorCompletionService verwenden. Es wartet auf Aufgaben, bis man true zurückgibt. Wenn dies nicht der Fall ist, gibt es schließlich false zurück.

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); 
    } 
} 

Dank für den Hinweis auf die ExecutorCompleteionService Klasse @Lav.

3

versuchen ExecutorCompletionService Mit ... so etwas wie

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 - Cool! Ich bin noch nicht auf "ExecutorCompletionService" gestoßen. –

1

Ich denke, Monitor-Logik könnte in diesem Fall gut funktionieren, obwohl es abhängig davon ist, dass Sie in der Lage sind, die Callables hinzuzufügen, um eine Referenz zu erhalten. Es könnte so aussehen in der parallelOR() -Methode:

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(); 
    } 

Während am Ende des Anrufs() -Methode in Ihrer Callables würden Sie haben:

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

Vielleicht liegt das an der Art der Einrichtung, aber wie würden Sie sicherstellen, dass 'isDone()' 'true' zurückgibt, wenn Sie innerhalb der Aufgabe signalisieren? – Collin

+0

Ich bin ein wenig unsicher, was du hier meinst. Ich würde diesen Block am Ende Ihrer call() Methode platzieren, so dass Sie explizit wissen, dass die aufrufbare Verarbeitung beendet ist. – SeanTheStudent