【问题标题】:Java Poller Thread cleanup before JVM shutdownJVM 关闭前的 Java Poller 线程清理
【发布时间】:2018-09-18 05:57:24
【问题描述】:

我有一个 Java 应用程序,它有一些无限运行的线程轮询后端数据库。当 JVM 关闭时(通过 weblogic UI),我需要为每个线程执行一些清理操作,例如连接到数据库并将正在运行的事务的状态从“进行中”设置为“未处理”。

有没有办法在每个线程中编写一个方法,当线程要被JVM停止时调用?

我知道关闭钩子,但我需要线程中变量的数据来了解在 Db 中要更改的状态,并且在关闭钩子的实现中,似乎启动了一个单独的钩子线程,它不会有来自原始线程。

【问题讨论】:

  • 如果您只需要处理正常关闭,您可以使用中断或原子布尔值来识别关闭。

标签: java multithreading shutdown-hook


【解决方案1】:

既然您可以访问这些线程(我的意思是您可以修改它们的代码),那么您可以:

  1. 不允许用户阻止应用程序正常退出。一旦他提示你这样做,你就会为他停止它。这是你需要钩子的地方。例如,如果应用程序基于JFrame,则添加windowClosing() 挂钩以优雅地停止正在运行的Threads,同时将框架的默认关闭操作设置为DO_NOTHING_ON_CLOSE .在下面的答案中查看我的代码...
  2. 无限循环中放置一个停止标志,使它们有限
  3. 当要关闭时,更改这些标志,以便每个 Thread 退出循环。
  4. 当退出循环时,Thread 将进行任何必要的更改(可以完全访问其所有变量),然后退出 run() 方法。
  5. 同时,在主要的Thread(即标记所有其他Threads 停止的Thread)中,您将前往join() 每个有限 Thread。 此方法允许等待 Thread 完全执行(即退出其 run() 方法)。
  6. 最后,主要的Thread 将调用例如 System.exit(0); 以退出整个应用程序,因为这应该是用户最后想要的操作。

注意:这假设用户将通过正常操作关闭应用程序。例如,如果用户打算通过 Windows 的任务管理器杀死应用程序,那么这种方法将不起作用。我不知道Threads 上的关闭挂钩...

遵循示例代码(阅读 cmets):

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class GraceDown {
    private static class MyThread extends Thread {
        private boolean keepGoing = true; //This is the stopping flag.
        private final int i; //In this case i marks the 'id' of the Thread, but it is also an arbitary variable that the Thread has acccess in its final moments...

        public MyThread(final int i) {
            this.i = i;
        }

        private synchronized boolean isKeepGoing() {
            return keepGoing; //Tells if we should exit the loop and start the stopping operation...
        }

        public synchronized void startShutdown() {
            keepGoing = false; //Tells we should exit the loop and start the stopping operation.
        }

        @Override
        public void run() {

            while (isKeepGoing()) { //After every complete loop, we check the if we can go on.
                //Your 'infinite' loop actions go in here:
                try { Thread.sleep(1000); } catch (final InterruptedException ie) {}
                System.out.println("Thread " + i + " running...");
            }

            //Your gracefull shutdown actions go here...
            System.out.println("Thread " + i + ": my stopping actions go here! Look, I have access to all my variables (such as i)! ;)");
        }
    }

    public static void main(final String[] args) {
        //Create and start the Threads:
        final MyThread[] myThreads = new MyThread[5];
        for (int i = 0; i < myThreads.length; ++i)
            myThreads[i] = new MyThread(i);
        for (int i = 0; i < myThreads.length; ++i)
            myThreads[i].start();

        final JFrame frame = new JFrame("Lets close me...");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //IMPORTANT STEP! This means we are going to close the application instead of it being closed automatically when the user presses to close the window...

        //Adding the 'hook':
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(final WindowEvent wevt) {
                System.out.println("Initializing shutdown...");
                //This is where the thread is going to stop all others...
                for (int i = 0; i < myThreads.length; ++i)
                    myThreads[i].startShutdown();

                //Wait for all threads to stop:
                for (int i = 0; i < myThreads.length; ++i)
                    try { myThreads[i].join(); } catch (final InterruptedException ie) { System.err.println("Error gracefully shutdowning the Thread!!!"); }

                //Now we can exit the JVM:
                System.out.println("Exiting JVM...");
                System.exit(0); //Code '0' indicates exiting without error(s).
            }
        });

        frame.getContentPane().add(new JLabel("Close this window and the Threads will shutdown gracefully...", JLabel.CENTER));
        frame.pack();
        frame.setLocationRelativeTo(null); //Put the packed frame on the center of the default screen.
        frame.setVisible(true);
    }
}

上面的内容应该是这样的:

线程 1 正在运行...
线程 4 正在运行...
线程 0 正在运行...
线程 2 正在运行...
线程 3 正在运行...
线程 3 正在运行...
线程 4 正在运行...
线程 2 正在运行...
线程 1 正在运行...
线程 0 正在运行...
正在初始化关机...
线程 2 正在运行...
线程2:我的停止动作到这里!看,我可以访问我所有的变量(例如 i)! ;)
线程 0 正在运行...
线程0:我的停止动作到这里!看,我可以访问我所有的变量(例如 i)! ;)
线程 1 正在运行...
线程 4 正在运行...
线程 3 正在运行...
线程3:我的停止动作到这里!看,我可以访问我所有的变量(例如 i)! ;)
线程4:我的停止动作到这里!看,我可以访问我所有的变量(例如 i)! ;)
线程1:我的停止动作到这里!看,我可以访问我所有的变量(例如 i)! ;)
退出 JVM...

这是我在您描述的问题中看到的最小解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    相关资源
    最近更新 更多