2015-01-01 6 views
7

Ich habe ein Stück von Java-Programm, das im wesentlichen macht folgendes:(Java) „remote“ eine Schleife verlass

public static void main(String[] args) 
{ 
    while(true) 
    { 
    // does stuff ... 
    } 
} 

Die Endlosschleife es ist beabsichtigt - wann wird unendlich Schleife allein das Programm verlassen. Zum größten Teil funktioniert es gut. Manchmal möchte ich das Programm jedoch zur Wartung herunterfahren, und wenn ich es herunter nehme, möchte ich sicherstellen, dass es den gesamten Code in der Schleife bis zum Ende durchläuft und dann beendet.

Ich frage mich, was die beste Lösung dafür ist. Eine Idee, die ich im Sinne habe, ist so etwas wie dies zu tun:

public static void main(String[] args) 
{ 
    File f = new File("C:\exit.txt"); 
    while(!f.exists()) 
    { 
     // does stuff ... 
    } 
} 

die im Grunde erlaubt es mir, anmutig aus der Schleife zu erhalten, indem eine Datei namens „exit.txt“ zu schaffen. Dies ist wahrscheinlich für meine Zwecke in Ordnung, aber ich würde gerne wissen, ob es bessere, alternative Methoden gibt.

+0

Gut für einfache Dinge, die Sie einen boolean wie 'downForMaintaince' verwenden können, und machen es'‘ wahr oder falsch als von Ihrer Notwendigkeit. dann 'while (! downForMaintance) {// endlosschleife}' –

+0

Wie manipuliere ich diese downForMaintaince-Variable von außen? – illu

+0

@AnirbanNag, Er meinte ohne interne "Hilfe", so dass externe Programme diesen Prozess signalisieren könnten. Diese Antworten könnten Sie leiten http://stackoverflow.com/questions/3244755/how-to-share-object-between-java-applications –

Antwort

-1

Für etwas schnell/schmutzig, Gebrauch Signale:

boolean done = false; 

// ... 

Signal.handle(new Signal("USR1"), new SignalHandler() { 
    @Override 
    public void handle(Signal signal) { 
     // signal triggered ... 
     done = true; 
    } 
}); 

// ... 

while(!done) { ... } 

, dann kill -USR1 _pid_ verwenden das Signal auszulösen.

+0

Das ist eine dumme Frage, aber welche Bibliothek brauche ich dafür? Ich habe versucht, diesen Code hier zu kompilieren (http://stackoverflow.com/a/1409195/4410951) und es beschwert sich, dass "Signal" nicht gelöst werden kann. – illu

+0

sun.misc ist das Paket. Also, importiere sun.misc. *; –

+3

Dies funktioniert nur auf einigen Plattformen. –

-1

Sie können ein AtomicBoolean wie im folgenden Testprogramm verwenden. Zum Suspendieren geben Sie einfach true in die Konsole ein, um den Typ false wieder aufzunehmen. Das Programm wird niemals beendet.

public class Test2 { 
public static void main(String[] args) { 
    final AtomicBoolean suspended = new AtomicBoolean(false); 

    new Thread() { 
     public void run() { 
      while (true) 
      { 
       Scanner sc = new Scanner(System.in); 
       boolean b = sc.nextBoolean(); 
       suspended.set(b); 
      } 
     } 
    }.start(); 


    while(true){ 
     if(!suspended.get()){ 
      System.out.println("working"); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
     else{ 
      //System.exit(0) //if you want to exit rather than suspend uncomment. 
     } 
    } 

} 

}

+0

Wie kann ich das Programm mit diesem Setup stoppen? – illu

+0

Möchten Sie System.exit tatsächlich tun? Wenn dies der Fall ist, fügen Sie eine else-Klausel mit System.exit (0) hinzu. Ich habe den Code so geändert, dass er die Änderung widerspiegelt, die Sie benötigen. – Dan

0

Sie Gebrauch von der Laufzeit Shutdown-Hook machen könnte. Auf diese Weise müssen Sie keine Konsoleneingabe verwenden, um die Schleife zu stoppen. Wenn JVM normal geschlossen wird, wird der Shutdown-Hook-Thread ausgeführt. Dieser Thread wartet auf das Ende der aktuellen Schleifeniteration. Beachten Sie, dass es einige Einschränkungen, wenn obwohl Haken: https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-

import java.util.concurrent.CountDownLatch; 

public class Test { 

    private volatile static CountDownLatch lastIterationLatch = null; 
    private static boolean stop = false; 

    public static void main(String [] args) throws Exception { 

     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       lastIterationLatch = new CountDownLatch(1); 
       try { 
        lastIterationLatch.await(); 
       } catch (Exception e) { 
        throw new RuntimeException(e); 
       } 
      } 
     }); 

     while(!stop) { 
      System.out.println("iteration start"); 
      Thread.sleep(200); 
      System.out.println("processing..."); 
      Thread.sleep(200); 
      System.out.println("processing..."); 
      Thread.sleep(200); 
      System.out.println("processing..."); 
      Thread.sleep(200); 
      System.out.println("iteration end"); 
      if(lastIterationLatch != null) { 
       stop = true; 
       lastIterationLatch.countDown(); 
      } 
     } 
    } 
} 
+0

Einverstanden, Sie könnten es so machen. Als ich die Antwort der Konsole gab, zeigte ich hauptsächlich auf ein Muster, wobei die Konsole nur ein mögliches Signal war. Es erlaubt auch Programm-Suspendierung und Wiederaufnahme, ohne zu beenden. – Dan

0

Ich denke, dass die WatchService, die in Java 7 eingeführt wurden hier von Nutzen sein kann (wenn Sie eine Datei-basierten Ansatz bevorzugen, das ist). Von der JavaDocs:

Ein Überwachungsdienst, der registrierte Objekte auf Änderungen und Ereignisse überwacht. Zum Beispiel kann ein Dateimanager einen Überwachungsdienst verwenden, um ein Verzeichnis auf Änderungen zu überwachen, so dass es seine Anzeige der Liste von Dateien aktualisieren kann, wenn Dateien erstellt oder gelöscht werden.

Grundsätzlich bedeutet dies, dass Sie eine WatchService einrichten können, die einen Ordner für Änderungen beobachten kann. Wenn eine Änderung auftritt, können Sie auswählen, welche Aktionen ausgeführt werden sollen.

Der folgende Code verwendet die WatchService, um einen angegebenen Ordner auf Änderungen zu überwachen. Wenn eine Änderung stattgefunden hat, führt sie eine Runnable aus, die der Aufrufer angegeben hat (die Methode runWhenItIsTimeToExit).

public class ExitChecker { 
    private final Path dir; 
    private final Executor executor; 
    private final WatchService watcher; 

    // Create the checker using the provided path but with some defaults for 
    // executor and watch service 
    public ExitChecker(final Path dir) throws IOException { 
     this(dir, FileSystems.getDefault().newWatchService(), Executors.newFixedThreadPool(1)); 
    } 

    // Create the checker using the provided path, watcher and executor 
    public ExitChecker(final Path dir, final WatchService watcher, final Executor executor) { 
     this.dir = dir; 
     this.watcher = watcher; 
     this.executor = executor; 
    } 

    // Wait for the folder to be modified, then invoke the provided runnable 
    public void runWhenItIsTimeToExit(final Runnable action) throws IOException { 
     // Listen on events in the provided folder 
     dir.register(watcher, 
       StandardWatchEventKinds.ENTRY_CREATE, 
       StandardWatchEventKinds.ENTRY_DELETE, 
       StandardWatchEventKinds.ENTRY_MODIFY); 

     // Run it async, otherwise the caller thread will be blocked 
     CompletableFuture.runAsync(() -> { 
      try { 
       watcher.take(); 
      } catch (InterruptedException e) { 
       // Ok, we got interrupted 
      } 
     }, executor).thenRunAsync(action); 
    } 
} 

Also, wie verwenden wir den Checker dann? Nun, der folgende Code zeigt dies:

public static void main(String... args) throws IOException, InterruptedException { 
    // Setup dirs in the home folder 
    final Path directory = Files.createDirectories(
      new File(System.getProperty("user.home") + "/.exittst").toPath()); 

    // In this case we use an AtomicBoolean to hold the "exit-status" 
    AtomicBoolean shouldExit = new AtomicBoolean(false); 

    // Start the exit checker, provide a Runnable that will be executed 
    // when it is time to exit the program 
    new ExitChecker(directory).runWhenItIsTimeToExit(() -> { 
     // This is where your exit code will end up. In this case we 
     // simply change the value of the AtomicBoolean 
     shouldExit.set(true); 
    }); 

    // Start processing 
    while (!shouldExit.get()) { 
     System.out.println("Do something in loop"); 
     Thread.sleep(1000); 
    } 

    System.out.println("Exiting"); 
} 

Schließlich, wie verlassen Sie das Programm dann? Berühren Sie einfach eine Datei im angegebenen Ordner.Beispiel:

cd ~/.exittst 
touch exit-now.please 

Ressourcen:

0

Man könnte hier einige ausgeklügelte Techniken einsetzen. Der Datei-Watchdog ist eine Option. RMI könnte ein anderer sein. Aber in der Tat sind die Mechanismen, die hier benötigt werden, ziemlich einfach, deshalb möchte ich eine andere (sehr einfache) Lösung vorschlagen.

Hinweis: Diese Lösung ist nur eine Option, die zeigt, dass es möglich ist es so zu tun. Es ist keine allgemeine Empfehlung, und ob es "gut" ist oder nicht, hängt vom Anwendungsfall ab.

Die Lösung basiert einfach auf Steckdosen. Die ServerSocket#accept Methode kapselt bereits die Funktionalität, die Sie wollen:

Hört auf eine Verbindung zu dieser Steckdose gemacht werden und akzeptiert es. Die Methode blockiert, bis eine Verbindung hergestellt wird.

Auf dieser Basis ist es trivial, eine solche „Fernbedienung“ zu erstellen: Der Server nur für eine Verbindung wartet, und ein Flag setzt, wenn die Verbindung geöffnet wird:

import java.io.IOException; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.concurrent.atomic.AtomicBoolean; 

class RemoteExitServer 
{ 
    private final AtomicBoolean flag = new AtomicBoolean(); 

    RemoteExitServer() 
    { 
     Thread t = new Thread(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       waitForConnection(); 
      } 
     }); 
     t.setDaemon(true); 
     t.start(); 
    } 

    private void waitForConnection() 
    { 
     ServerSocket server = null; 
     Socket socket = null; 
     try 
     { 
      server = new ServerSocket(1234); 
      socket = server.accept(); 
      flag.set(true); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
     } 
     finally 
     { 
      if (server != null) 
      { 
       try 
       { 
        server.close(); 
       } 
       catch (IOException e) 
       { 
        e.printStackTrace(); 
       } 
      } 
      if (socket != null) 
      { 
       try 
       { 
        socket.close(); 
       } 
       catch (IOException e) 
       { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 

    boolean shouldExit() 
    { 
     return flag.get(); 
    } 
} 

Der Client macht genau das: Es eine Verbindung öffnet, und nichts anderes

import java.io.IOException; 
import java.net.Socket; 

public class RemoteExitClient 
{ 
    public static void main(String[] args) 
    { 
     Socket socket = null; 
     try 
     { 
      socket = new Socket("localhost", 1234); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
     finally 
     { 
      if (socket != null) 
      { 
       try 
       { 
        socket.close(); 
       } 
       catch (IOException e) 
       { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
} 

Die Anwendung ist dann auch sehr einfach:

75.293.210

(Der Code vorgenommen werden könnten, noch prägnanter mit Try-with-Ressourcen, aber dies sollte nicht Sache hier)

+0

Es scheint, als ob jede Antwort bisher mindestens einmal abgelehnt wurde. Ich würde gerne eine Antwort sehen, die die Downvoter nicht als "nicht hilfreich" empfinden. Aus systematischer Sicht gibt es höchstens drei Möglichkeiten, mit einer laufenden Java-Anwendung zu kommunizieren: 1. über Dateien, 2. über Netzwerk, 3. vielleicht irgendwie mit JNI-Magie. Letzteres scheint unpraktisch. Der dateibasierte Ansatz sieht für mich etwas ungeschickt aus. Was bleibt, ist der netzwerkbasierte Ansatz (egal ob über RMI oder einen eigenen Socket). Und das ist so nah an * "Fernsteuerung" * wie es nur geht. – Marco13