[英](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
資源:
WatchService
在Mac OS X上運行緩慢 這里可以采用一些復雜的技術。 文件看門狗是一種選擇。 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.