简体   繁体   English

Java Shutdown Hooks

[英]Java Shutdown Hooks

Using IntelliJ IDE, java 1.8, lang level 6 in IDE (and whatever is default at the command prompt). 在IDE中使用IntelliJ IDE,java 1.8,lang level 6(以及命令提示符下的默认值)。

Compiling and running from commandline (Windows 7) with: 从命令行(Windows 7)编译并运行:

javac -cp . Main.java
java -cp . Main

Given the following code, what I'd like to do is shut down all of the running threads in the app (and maybe I don't need to do so, if the OS or JVM does it for me). 鉴于以下代码,我想要做的是关闭应用程序中所有正在运行的线程(如果操作系统或JVM为我做的话,我可能不需要这样做)。 I have a thread MyThread, which in turn, holds a list of threads it's managing (and when interrupted by the main, will send an interrupt to each thread in its list). 我有一个线程MyThread,它反过来拥有一个它正在管理的线程列表(当被主节点中断时,它会向其列表中的每个线程发送一个中断)。

When I run the code, I get the output Keeping busy. 当我运行代码时,我得到输出Keeping busy. printed every 2s. 每2秒打印一次。 When I press CTRL-C, the shutdown hook runs (I put println messages in there to confirm) but that is all of the output I see. 当我按下CTRL-C时,关闭钩子运行(我把println消息放在那里确认)但这就是我看到的所有输出。 I never see the Cancelled. 我从未见过被Cancelled. nr the end of the main()... so I must also presume that the Cancel method is also not being run. nr main()的结尾...所以我还必须假设Cancel方法也没有运行。

So, first of all, do I need to care about this? 所以,首先,我需要关心这个吗? Am I okay with just looping (in main) on isInterrupted and letting the program cease? 我是否可以在isInterrupted上循环(在main中)并让程序停止? How do all of the sub-threads get cancelled? 如何取消所有子线程?

import java.util.HashMap;

class MyThread extends Thread {}

public class Main
{
    private static volatile boolean running = true;
    // List of active threads.
    private HashMap<String, Thread> threads = new HashMap<String, Thread>();

    public static void main(String[] args) throws InterruptedException
    {
        // Get an instance of the Main class.
        Main myApp = new Main();

        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            public void run()
            {
                running = false;
            }
        });

        // Loop until system shutdown.
        while (running && !Thread.currentThread().isInterrupted())
        {
            myApp.CreateMyThread();

            // In real code, I have a wait() here, rather than the following lines.

            System.out.println("Keeping busy.");
            Thread.sleep(2000);
        }

        System.out.println("Cancelled.");

        // Kill all threads.
        myApp.Cancel();

        System.exit(0);
    }

    private void Cancel()
    {
        for (Thread t : threads.values())
        {
            t.interrupt();
        }
    }

    private void CreateMyThread()
    {
        if (threads.get("mythread") instanceof MyThread)
        {
            // Already exists.
            return;
        }

        MyThread t = new MyThread();
        t.start();
        threads.put("mythread", t);
    }
}

EDIT : 编辑

So, I changed my code as follows. 所以,我改变了我的代码如下。 Created a Threadlist class containing the hashmap, with methods using keyword 'synchronized' to add/remove a thread and to cancel all threads. 创建了一个包含hashmap的Threadlist类,其中的方法使用关键字'synchronized'来添加/删除线程并取消所有线程。 The main contains a static instance of this class. main包含此类的静态实例。 My shutdown handler then calls the CancelThreads method to shut down all running threads. 我的关闭处理程序然后调用CancelThreads方法来关闭所有正在运行的线程。 Question: is this truly thread-safe, ie: is having synchronized on methods actually blocking one method from being run when another is in progress? 问题:这是真正的线程安全吗,即:是否正在同步实际阻止一个方法运行的方法在另一个方法正在进行时?

import java.util.HashMap;

class MyThread extends Thread {}

class Threadlist
{
    // List of threads.
    private HashMap<String, Thread> threads = new HashMap<String, Thread>();

    public synchronized void AddThread(String name, Thread thread)
    {
        threads.put(name, thread);
    }

    public synchronized Thread GetThread(String name)
    {
        return threads.get(name);
    }

    public synchronized void CancelThreads()
    {
        for (Thread t : threads.values())
        {
            System.out.printf("Killing a thread by the name of '%s'\n", t.getName());
            t.interrupt();
        }
    }
}

public class Main
{
    private static volatile boolean running = true;
    // List of active threads.
    private static Threadlist threads = new Threadlist();

    public static void main(String[] args) throws InterruptedException
    {
        // Get an instance of the Main class.
        Main myApp = new Main();

        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            public void run()
            {
                threads.CancelThreads();
                running = false;
            }
        });

        // Loop until system shutdown.
        while (running && !Thread.currentThread().isInterrupted())
        {
            myApp.CreateMyThread();

            // In real code, I have a wait() here, rather than the following lines.

            System.out.println("Keeping busy.");
            Thread.sleep(2000);
        }

        System.out.println("Cancelled.");

        System.exit(0);
    }

    private void CreateMyThread()
    {
        if (threads.GetThread("mythread") instanceof MyThread)
        {
            // Already exists.
            return;
        }

        MyThread t = new MyThread();
        t.start();
        threads.AddThread("mythread", t);

}

} }

I had also begun creating a ShutdownHook class, extending Thread, which would have accepted main's threads object via the constructor and then cancelled the threads in the run() method, but I couldn't see how to change the main->running value at the same time, hence, my approach above. 我也开始创建一个ShutdownHook类,扩展Thread,它会通过构造函数接受main的线程对象,然后取消run()方法中的线程,但我看不到如何更改main-> running值at at同时,因此,我的方法在上面。

Your shutdown hook is a separate Thread that is kicked off when shutdown occurs (hopefully, no guarantee). 你的关闭钩子是一个单独的线程,在关闭时启动(希望,不能保证)。 Your main thread, meanwhile, is still progressing, but it is about to get killed. 与此同时,你的主线仍在进步,但它即将被杀。 This MIGHT work, but the interrupt probably won't give you 2 seconds you have there in your code before it kills the main thread. 这可能工作,但是在你的代码杀死主线程之前,中断可能不会给你2秒钟。

Instead, you should share the list of spawned threads with your shutdown hook and have that hook attempt to interrupt/shutdown the spawned threads directly. 相反,您应该使用关闭钩子共享生成的线程列表,并让该钩子尝试直接中断/关闭生成的线程。

Once shutdown hooks finish running the VM halts (there is actually another step in between but not relevant for this discussion). 一旦关闭挂钩完成运行,VM就会停止(实际上还有另一个步骤但与此讨论无关)。

The key here is your shutdown hook needs to live long enough for other thread to clean up, you can do this by having your shutdown hook call Thread.join(long) on the other thread, with a timeout. 这里的关键是你的关闭钩子需要足够长的时间来让其他线程清理,你可以通过让你的关闭钩子在另一个线程上调用Thread.join(long)来实现这一点,并且超时。 This will allow the shutdown hook to end after the other thread or when the specified timeout is exceeded. 这将允许关闭挂钩在另一个线程之后或超过指定的超时时结束。

Blocking the shutdown processes is usually considered to be not user friendly. 阻止关闭过程通常被认为不是用户友好的。 Your shutdown hook should wake up or signal the other thread before joining on it, how this is done is up to you ( interrupt() , wait() / notifyAll() pairing, or using a class from java.util.concurrent package. 您的关闭挂钩应该在加入之前唤醒或发出其他线程的信号,这是如何完成的取决于您( interrupt()wait() / notifyAll()配对或使用java.util.concurrent包中的类。

Example: 例:

    final Object lock = new Object(); //Lock object for synchronization.
    final Thread mainThread = Thread.currentThread(); //Reference to the current thread.

    Runtime.getRuntime().addShutdownHook(new Thread()
    {
        public void run()
        {
            try{
                synchronized(lock){
                  running = false;
                  lock.notifyAll();
                }

                mainThread.join(4000);
            }catch(InterruptedException){
               //Interrupted.
            }
        }
    });

    synchronized(lock){
        while (running && !Thread.currentThread().isInterrupted())
        {
            myApp.CreateMyThread();

            System.out.println("Keeping busy.");
            lock.wait(2000); 
        }
     }

There are some issues with the above example, a big one being that wait() is not useful as a timer ( wait() is allowed to spuriously wake up), so if you really need to have it be every 2 seconds the logic is more complicated. 上面的例子有一些问题,一个很重要的问题是wait()作为一个定时器没有用wait()允许wait()被假性地唤醒),所以如果你真的需要每2秒就有一个逻辑是更复杂。 However, this outlines the basics of getting it to work. 但是,这概述了使其工作的基础知识。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM