2015-03-08 6 views
5

Ich habe ein großes Code-Segment, das keine Schleife ist, nur eine Liste von Befehlen, die einmal passiert, aber einige Zeit braucht. Ich brauche es entweder zu pausieren oder zu einem beliebigen Zeitpunkt zu beenden, basierend auf einem sich ändernden booleschen Wert. Ich könnte einen anderen Thread verwenden, um diesen Code zu suspendieren, fortzusetzen und zu stoppen, aber diese Methoden sind veraltet, weshalb ich sie vermeiden möchte. Ich könnte den Booleschen Wert zwischen jeder Codezeile überprüfen, aber ich hoffe auf eine elegantere Lösung. Gibt es einen guten Weg, dies zu tun?Alternative zu Thread.suspend() und .resume()

+0

Wie werden Sie jeden Befehl ausführen? Wenn das eine separate Klasse wie "Action" oder "Command" ist, können Sie dort den Check durchführen. Ansonsten kann man nicht viel tun ... wie in anderen Antworten geschrieben. – Jayan

+0

Ich bin Code-unabhängig für einen FRC-Roboter. Einiges davon ist es, die Motoren auf eine Geschwindigkeit zu setzen und zu warten, andere Bits sollen die Motorwerte einstellen, bis die Sensoren einen bestimmten Wert ergeben. – yesennes

Antwort

1

Die korrekte Art, einen Thread zu unterbrechen (in diesem Fall, um ihn anzuhalten oder zu stoppen), ist natürlich Thread#interrupt(). Es ist so konzipiert, dass Sie sichere Punkte definieren können, an denen der Thread unterbrochen werden kann, was für Sie natürlich der Punkt zwischen jeder Aufgabe ist. Um zu vermeiden, dass Sie Ihre Variablen zwischen den einzelnen Aufgaben manuell überprüfen müssen, können Sie Ihre Aufgaben einfach als eine Liste von Runnables speichern und Ihre Position in der Liste ab dem Zeitpunkt Ihrer Beendigung speichern , wie folgt:

public class Foo { 
    public static void runTask(Runnable task) throws InterruptedException { 
     task.run(); 
     if (Thread.interrupted()) throw new InterruptedException(); 
    } 
    Runnable[] frobnicateTasks = new Runnable[] { 
     () -> { System.out.println("task1"); }, 
     () -> { Thread.currentThread().interrupt(); }, //Interrupt self only as example 
     () -> { System.out.println("task2"); } 
    }; 
    public int frobnicate() { 
     return resumeFrobnicate(0); 
    } 
    public int resumeFrobnicate(int taskPos) { 
     try { 
      while (taskPos < frobnicateTasks.length) 
       runTask(frobnicateTasks[taskPos++]); 
     } catch (InterruptedException ex) { 
     } 
     if (taskPos == frobnicateTasks.length) { 
      return -1; //done 
     } 
     return taskPos; 
    } 
    public static void main(String[] args) { 
     Foo foo = new Foo(); 
     int progress = foo.frobnicate(); 
     while (progress != -1) { 
      System.out.println("Paused"); 
      progress = foo.resumeFrobnicate(progress); 
     } 
     System.out.println("Done"); 
    } 
} 
--> 
task1 
Paused 
task2 
Done 
+1

Ich stimme damit nicht überein. Interrupt hat das Problem, dass es ... im Grunde ... einige blockierende Operationen unterbricht und den unterbrochenen Thread in einem Zustand belässt, in dem die Wiederaufnahme (zumindest) kompliziert ist. Das Testen eines booleschen Werts ist ein besserer Ansatz für eine Pause/Wiederaufnahme. –

+0

@StephenC Alle Blockierungsoperationen, die möglicherweise unterbrochen werden, erzwingen die Überprüfung auf InterruptedException und entscheiden, was zu tun ist. Es klingt für mich so, als ob sie solche Operationen verwenden würden, dann ist es das gewünschte Verhalten, sie zu unterbrechen (obwohl sie möglicherweise einige Mittel zur Signalisierung hinzufügen müssen, um taskPos nicht zu erhöhen); Wenn dies nicht das gewünschte Verhalten ist, können sie 'Thread.interrupted()' leicht durch eine spezielle Feldabfrage ersetzen. – Vitruvius

+0

In diesem Fall möchte das OP suspend/resume, nicht unterbrechen/neu starten Semantik. Die Frage besagt, dass er nach einem Ersatz für 'Thread.suspend()' sucht, und so weiter. –

1

Sie können wait/notify als Alternative zum Suspend/Resume verwenden. Statt Stopp können Sie ein Flag setzen, mitteilen und eine Ausnahme von angemeldeten Thread werfen

+1

Denken Sie daran, dass dies kein Code-Code ist. Es gibt keinen geeigneten Ort, an dem das Überprüfen einer Flagge oft geschieht. Wenn ich deine Lösung verstehe, hätte ich die Flagge jede zweite Zeile überprüft, an diesem Punkt könnte ich auch nur den Booleschen Wert überprüfen. – yesennes

1

Es gibt eine ausgezeichnete Dokumentation darüber, warum Thread.stop(), Thread.pause() und Thread.resume() sind veraltet:

Java Thread Primitive Deprecation

Es gibt auch eine Alternative für /Thread.resume() mit wait und notify erklärt.

+0

Ich kann nicht denken, wie man in meiner Situation warte und benachrichtige. Wenn Sie können, würde ich mich freuen zu wissen. – yesennes

+0

@yesennes Siehe die Antwort https://stackoverflow.com/questions/28922040/alternative-to-thread-suspend-and-resume/45786529#45786529, um dies mithilfe von wait/notify zu tun. – zafar142003

4

Ich könnte den Boolean zwischen jeder Codezeile überprüfen, aber ich hoffe auf eine elegantere Lösung. Gibt es einen guten Weg, dies zu tun?

Leider nein.

Um Pause/Resume zu ersetzen, ist es wichtig, dass ein Thread einen anderen Thread "pausiert", ohne den Code des zweiten Threads zu verwenden.

Dies ist in Java nicht möglich, wie es derzeit spezifiziert und implementiert ist.

Die veralteten Thread-Methoden sind die einzige Möglichkeit, einen Thread zu beenden, zu stoppen/anzuhalten, einen anderen Thread fortzusetzen ... wenn der andere Thread nicht aktiv kooperiert. Sie waren veraltet, weil (in Kürze) diese Art von Kontrolle nicht in jeder gängigen Generation Mainstream JVM sicher implementiert werden kann.

Das Beste, was Sie tun können, ist das Umbrechen der Logik für Pause/Fortsetzen in einer Methode, die der zweite Thread an geeigneten Punkten aufruft. (Dies sollte wahrscheinlich warten/benachrichtigen ... oder gleichwertig ... um die Wiederaufnahme zu behandeln. Aber warten/notify per se adressiert nicht Ihre klare Anforderung zum Anhalten/Fortsetzen eines nicht kooperierenden Threads.)


1 - Sie benötigen etwas wie die Isolates-API, um dies zu tun. Es gab Versuche, es in Forschungsprojekten umzusetzen, aber AFAIK Isolates wurde nie in einer Produktions-JVM implementiert.

+1

Bitte erläutern Sie den Downvote. Sie mögen diese Antwort nicht * mögen *, aber die Ablehnung wird die Realität nicht ändern. Java bietet Ihnen keine wesentlich bessere Alternative als die, die Sie gerade verwenden. –

+0

Das war ein Miss Klick. Ich wollte es abstimmen. Ich mochte die Antwort nicht, aber sie war wahr und auf meine Situation anwendbar. Ich hätte mich jetzt darum kümmern müssen. – yesennes

+0

Es gibt eine ungefähre Lösung für dieses Problem. Bitte beachten Sie die Antwort https://stackoverflow.com/questions/28922040/alternative-to-thread-suspend-and-resume/45786529#45786529 – zafar142003

1

Ich habe eine Idee, wie Sie dies mit AspectJ tun.Die Lösung, die ich unten skizziere, erlaubt Ihnen, einen anderen Thread anzuhalten/fortzusetzen. Diese Pause tritt erst beim nächsten Methodenaufruf aus der Zielmethode in Kraft, nachdem Sie die Pause-Routine aufgerufen haben. Dies wird nicht erfordern boolesche Überprüfungen nach jeder Zeile des Codes Ihrer Zielmethode. Ich verwende wait/notify, um dies zu tun.

Ich verwende Spring in dem folgenden Beispiel.

Zuerst wickeln Sie Ihre Zielmethode in eine ausführbare Datei.

Als nächstes machen Sie einen Aspekt, der bei jedem Methodenaufruf innerhalb Ihrer Zielmethode bedingt aufgerufen würde.

@Aspect 
@Component 
public class HandlerAspect { 

    public static volatile boolean stop=false; 
    public static volatile String monitor=""; 



    @Pointcut("withincode(public void com.app.inter.thread.communication.runnables.TestRunnable.run()) " 
      + "&& call(* *.*(*)) " 
      + "&& if()") 
    public static boolean stopAspect(){ 
     return stop; 
    } 
    @Before("stopAspect()") 
    public void beforeAdvice(JoinPoint jp) { 
     try { 
      System.out.println("Waiting"); 

      synchronized(monitor){ 
       monitor.wait(); 
      } 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     stop=false; 
    } 
} 

Nun, wenn Sie Ihren Thread zu unterbrechen wollen, setzen Sie einfach den Wert von stop zu wahren, und umgekehrt.

@Service 
public class Driver { 
    @Autowired 
    private HandlerAspect aspect; 

    @PostConstruct 
    public void init(){ 
     Scanner scanner = new Scanner(System.in); 
     int i=-1; 
     TestRunnable runnable = new TestRunnable(); 
     Thread thread= new Thread(runnable); 
     thread.start(); 
     aspect.monitor="MONITOR"; 
     while((i=scanner.nextInt())!=0){ 
      switch(i){ 
       case 1: 
        aspect.stop=true; 
        break;  
       case 2: 

        aspect.stop=false;     
        synchronized(aspect.monitor){ 
         aspect.monitor.notify(); 
        } 
        break; 
      } 
     } 
     // in case the main thread is stopped 
     // while the other thread is in wait state 
     synchronized(aspect.monitor){ 
      aspect.monitor.notify(); 
     } 
     thread.interrupt(); 

    } 
} 

Die Ausgabe ist wie folgt:

... 
Working 400000001 
1Working 410000001 
Working 420000001 
Working 430000001 
Working 440000001 
Working 450000001 
Working 460000001 
Working 470000001 
Waiting 
2 
Working 480000001 
Working 490000001 
0 

Arbeitscode ist bei https://github.com/zafar142007/InterThreadCommunication