簡體   English   中英

(Java)退出循環“遠程”

[英](Java) exiting a loop “remotely”

我有一個Java程序基本上執行以下操作:

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

無限循環是設計的 - 當單獨留下時,程序將無限循環。 在大多數情況下它工作正常。 但是,有時候我想把程序用於維護,當我把它關閉時,我想確保它遍歷循環中的所有代碼然后退出。

我想知道什么是最好的解決方案。 我想到的一個想法是做這樣的事情:

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

這基本上允許我通過創建一個名為“exit.txt”的文件來優雅地離開循環。 這對我的目的來說可能沒問題,但我想知道是否有更好的替代方法。

您可以使用運行時關閉鈎子。 這樣您就不需要使用控制台輸入來停止循環。 如果正常關閉JVM,則將運行shutdown hook thread。 該線程將等待當前循環迭代的結束。 請記住,使用掛鈎時存在一些限制: 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();
           }
        }
    }
}

我認為Java 7中引入的WatchService可能在這里有用(如果您更喜歡基於文件的方法)。 來自JavaDocs

監視已注冊對象的更改和事件的監視服務。 例如,文件管理器可以使用監視服務來監視目錄以進行更改,以便在創建或刪除文件時它可以更新其文件列表的顯示。

基本上這意味着你可以設置一個WatchService來監視文件夾的變化。 發生更改時,您可以選擇要執行的操作。

以下代碼使用WatchService監視指定的文件夾以進行更改。 發生更改時,它會執行調用者提供的Runnable (方法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);
    }
}

那么,我們如何使用檢查器呢? 好吧,以下代碼說明了這一點:

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

最后,你如何退出該計划呢? 只需觸摸指定文件夾中的文件即可。 例:

cd ~/.exittst
touch exit-now.please

資源:

這里可以采用一些復雜的技術。 文件看門狗是一種選擇。 RMI可能是另一個。 但實際上,這里需要的機制非常簡單,所以我想提出另一種(非常簡單的)解決方案。

注意:此解決方案只是一個選項,表明可以這樣做。 這不是一般性建議,是否“好”取決於應用案例。

該解決方案僅基於套接字 ServerSocket#accept方法已經封裝了您想要的功能:

偵聽對此套接字的連接並接受它。 該方法將阻塞,直到建立連接。

基於此,創建這樣的“遠程控制”是微不足道的:服務器只是等待連接,並在打開連接時設置標志:

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

客戶端就是這樣做的:它打開一個連接,沒有別的

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

該應用程序也非常簡單:

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

(使用try-with-resources可以使代碼更加簡潔,但這在這里無關緊要)

對於快速/臟的東西,請使用信號:

boolean done = false;

// ...

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

// ...

while(!done) { ... }

然后,使用kill -USR1 _pid_來觸發信號。

您可以在下面的測試程序中使用AtomicBoolean。 要暫停,只需在控制台中輸入true即可恢復類型false。 該計划永遠不會退出。

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

}

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM