2010-07-05 13 views
5

Ho un metodo che, concettualmente, sembra qualcosa di simile:Come scrivere metodi interrompibili

Object f(Object o1) { 
    Object o2 = longProcess1(o1); 
    Object o3 = longProcess2(o2); 
    return longProcess3(o3); 
} 

Dove i processi stessi potrebbe anche essere composto:

Object longProcess1(Object o1) { 
    Object o2 = longSubProcess1(o1); 
    return longSubProcess2(o2); 
} 

E così via, con il diverso processi potenzialmente seduti in diversi moduli. La maggior parte dei processi è lunga perché sono computazionalmente costosi, non vincolati all'IO.

Fin qui tutto bene, ma ora voglio f nel suo complesso per essere interrompibile. The recommended Java way to do that controlla periodicamente la flag interrotta con Thread.interrupted(). E 'piuttosto semplice, ma può rapidamente diventare ingombrante se ho bisogno di cambiare i miei metodi per qualcosa di simile:

Object f(Object o1) { 
    Object o2 = longProcess1(o1); 
    if (Thread.interrupted()) throw new InterruptedException(); 
    Object o3 = longProcess2(o2); 
    if (Thread.interrupted()) throw new InterruptedException(); 
    return longProcess3(o3); 
} 

Object longProcess1(Object o1) { 
    Object o2 = longSubProcess1(o1); 
    if (Thread.interrupted()) throw new InterruptedException(); 
    return longSubProcess2(o2); 
} 

... 

Ora, capisco il razionale per lavorare come quella - mi permette di controllare meglio quando il InterruptedException (ad esempio) verrà lanciato, evitando di lasciare oggetti in stati incoerenti - ma Sono curioso di sapere se esiste un modo più elegante per farlo *.

* In Java, non AspectJ, che credo sia molto appropriato qui ma sono bloccato con Java.

+0

sarei preoccupato di situazioni di stallo con il codice che contiene le discussioni che ref vicenda. –

risposta

7

Si potrebbe utilizzare un'interfaccia e un proxy dinamico:

public class Wrapper { 
    public static <T> T wrap(Class<T> intf, final T impl) { 
     ClassLoader cl = Thread.currentThread().getContextClassLoader(); 
     Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] {intf}, 
       new InvocationHandler() { 
      public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable { 
       if (Thread.interrupted()) { 
        throw new InterruptedException(); 
       } 
       return method.invoke(impl, args); 
      } 
     }); 
     return intf.cast(proxy); 
    } 
} 

interface Processes { 
    Object longProcess1(Object o); 
    ... 
} 

public class ProcessesImpl implement Processes { 
    Processes self = Wrapper.wrap(Processes.class, this); 

    public Object f(Object o1) { 
     Object o2 = self.longProcess1(o1); 
     Object o3 = self.longProcess2(o2); 
     return self.longProcess3(o3); 
    } 

    public Object longProcess1(Object o1) { 
     Object o2 = self.longSubProcess1(o1); 
     return self.longSubProcess2(o2); 
    } 

    .... 
} 
+0

Interessante, ma ancora un po 'macchinoso. Dovrei aggiungere un campo per tutte le classi pertinenti e qualificare molte chiamate di metodo. I metodi risultanti * sono * più eleganti, però, te lo darò! – Oak

0

ho ricevuto questo diritto che in sequenza i metodi run che sono allo stesso livello di nidificazione? In tal caso, perché non implementare i tuoi metodi di calcolo come istanze java.lang.Runnable, organizzarli in elenchi e avviarli nel ciclo? Allora avresti solo un posto con il controllo Thread.interrupted().

È possibile utilizzare java.util.concurrent.ExecutorService per facilitare il controllo delle attività di calcolo.

aggiornato con un esempio:

import java.util.ArrayList; 
import java.util.List; 

public class Test { 

    public static void main(String[] args) { 
     List<CompoundProcess> subProcesses1 = new ArrayList<CompoundProcess>(); 
     subProcesses1.add(new CompoundProcess() { 
      public void run() { 
       System.out.println("Process 1.1"); 
      } 
     }); 
     subProcesses1.add(new CompoundProcess() { 
      public void run() { 
       System.out.println("Process 1.2"); 
      } 
     }); 

     List<CompoundProcess> subProcesses2 = new ArrayList<CompoundProcess>(); 
     subProcesses2.add(new CompoundProcess() { 
      public void run() { 
       System.out.println("Process 2.1"); 
      } 
     }); 
     subProcesses2.add(new CompoundProcess() { 
      public void run() { 
       System.out.println("Process 2.2"); 
      } 
     }); 

     List<CompoundProcess> processes1 = new ArrayList<CompoundProcess>() {}; 
     processes1.add(new CompoundProcess(subProcesses1)); 
     processes1.add(new CompoundProcess(subProcesses2)); 

     CompoundProcess process = new CompoundProcess(processes1); 
     process.run(); 
    } 


    static class CompoundProcess implements Runnable { 

     private List<CompoundProcess> processes = new ArrayList<CompoundProcess>(); 

     public CompoundProcess() { 
     } 

     public CompoundProcess(List<CompoundProcess> processes) { 
      this.processes = processes; 
     } 

     public void run() { 
      for (Runnable process : processes) { 
       if (Thread.interrupted()) { 
        throw new RuntimeException("The processing was interrupted"); 
       } else { 
        process.run(); 
       } 
      } 
     } 
    } 

} 
+0

No, i processi sono in molti posti diversi - Ho provato a dimostrarlo mostrando che 'longProcess1' è di per sé composto, forse non ero abbastanza chiaro - quindi ho modificato la domanda per chiarire. – Oak

+0

Una volta che i processi allo stesso livello vengono eseguiti in sequenza, non importa che vengano avviati in luoghi diversi. Ho aggiornato la mia risposta con un esempio. È possibile nidificare i processi al livello desiderato. – fnt

Problemi correlati