6

szukam wskazówek dla problemu logicznie równoważna następującej:Jak przeprowadzić ocenę zwarcia w Javie na dwóch równoległych wątkach zwracających wartości logiczne?

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 
} 

Powyższa konstrukcja daje poprawny wynik ale zawsze czeka na Taska aby zakończyć nawet jeśli wynik jest już znany od taskB ma zakończono.

Czy istnieje lepsza konstrukcja, która pozwoli na zwrócenie wartości , jeśli jeden z wątków zwróci wartość true bez czekania na zakończenie drugiego wątku?

(Zaangażowaną platformę jest Android, jeśli ma to wpływ na wynik).

+0

można zmodyfikować zadania? – Collin

+0

Więc nie chcesz czekać na wątki A i B, aby ukończyć przed otrzymaniem valus ??? i chcesz, kiedy A zostanie zrobione, dostaniesz wartość i na odwrót? –

+0

Wygląda na to, że jeśli taskB zakończy się jako pierwszy i będzie prawdą, to możesz wykonać zwarcie i nie czekać na zakończenie zadania A. – Collin

Odpowiedz

2

Oto implementacja ParallelOr użyciu ExecutorCompletionService. Czeka na zadania, dopóki nie zwróci true. Jeśli nie, to ostatecznie zwraca 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); 
    } 
} 

Dzięki @Lav za wskazanie klasy ExecutorCompleteionService.

3

spróbować Korzystanie ExecutorCompletionService ... coś jak

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 - Super! Nie natknąłem się wcześniej na "ExecutorCompletionService". –

1

Myślę, że logika monitorująca może dobrze działać w tym przypadku, chociaż jest zależna od tego, czy możesz dodać zmiany, aby otrzymać referencję. To może wyglądać tak w metodzie 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(); 
    } 

O ile na koniec (metody w swoim callables połączeń) byś mieć:

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

Być może jest to nieodłączne ze względu na sposób, w jaki zostało skonfigurowane, ale w jaki sposób zapewnilibyście, że 'isDone()' zwróciłoby 'true', jeśli sygnalizujesz z wnętrza zadania? – Collin

+0

Nie jestem pewien, co masz na myśli. Umieściłbym ten blok na samym końcu metody call(), tak abyś wyraźnie wiedział, że przetwarzanie zostało zakończone. – SeanTheStudent