(Java) exiting the "remotely" loop

I have a part of a Java program that essentially does the following:

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

An infinite loop exists by design - if you leave it alone, the program will spin endlessly. For the most part, it works great. However, sometimes I want to disable the program for maintenance, and when I remove it, I want to make sure that it runs all the code in a loop to the end, and then quits.

I am wondering what is the best solution for this. One of the ideas I have in mind is to do something like this:

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

which basically allows me to gracefully exit the loop by creating a file called "exit.txt". This may be good for my purposes, but I would like to know if there are better alternative methods.

+7
java infinite-loop
source share
5 answers

You can use the shutdown hook. This way you will not need to use console input to stop the loop. If the JVM closes normally, then the disconnect hook thread will execute. This thread will wait for the end of the current loop iteration. Keep in mind that there are some limitations when using hooks: 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
source share

I think the WatchService that was introduced in Java 7 might be useful here (if you prefer a file based approach). From JavaDocs :

A watch service that monitors registered objects for changes and events. For example, a file manager might use a viewer to monitor a directory for changes so that it can update the display of a list of files when creating or deleting files.

This basically means that you can set up a WatchService that can view the folder for changes. When a change occurs, you can choose what action to take.

The following code uses WatchService to track the specified folder for changes. When a change has occurred, it executes the Runnable that the caller provided (the runWhenItIsTimeToExit method).

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

So how can we use a controller? Well, the following code illustrates this:

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

Finally, how do you exit the program? Well, just tap the file in the specified folder. Example:

 cd ~/.exittst touch exit-now.please 

Resources

0
source share

Some sophisticated methods can be used here. A watchdog file is one option. RMI may be different. But in fact, the mechanisms required here are quite simple, so I would like to propose another (very simple) solution.

Note. This solution is just one option showing that this can be done like this. This is not a general recommendation, and whether it is “good” or not depends on the application.

The solution is simply based on Sockets. The ServerSocket#accept method already encapsulates the necessary functions:

Listens to the connection to this socket and accepts it. The method blocks until a connection is made.

Based on this, it is trivial to create such a "remote control": the server just waits for a connection and sets a flag when opening a connection:

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

The client does just that: he opens the connection and nothing else

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

The application is also very simple:

 public class RemoteExitTest { public static void main(String[] args) { RemoteExitServer e = new RemoteExitServer(); while (!e.shouldExit()) { System.out.println("Working..."); try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } } System.out.println("done"); } } 

(The code may be even more concise with try-with-resources, but that doesn't matter here)

0
source share

For something quick / dirty use Signals:

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

Then use kill -USR1 _pid_ to trigger the signal.

-one
source share

You can use AtomicBoolean, as in the test program below. To pause, just type true in the console to resume false. The program will never exit.

 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. } } } 

}

-one
source share

All Articles