0

我有一个多线程程序,它基本上有两个无限循环线程加上一些 GUI(Swing)。我的两个无限循环线程有自己的 log4j 记录器,如下所示:

    static final Category LOG = Category.getInstance(RReceiver.class);

在 GUI 中我检测到退出键时,我只执行 System.exit(0):

public boolean dispatchKeyEvent(KeyEvent e) {
    if (e.getKeyCode()!=27 && e.getKeyCode()!=KeyEvent.VK_BACK_SPACE) return false; 
    System.exit(0);
    return true;
}

控制台中的效果是这样的:

调试 13:07:00,940 [接收器] (RReceiver.java:93) - 得到响应 len:38
调试 13:07:01,044 [接收器] (RReceiver.java:93) - 得到响应 len:38
调试 13:07:01,045 [接收器] (RReceiver.java:93) - 新状态开发 4
log4j:WARN 找不到记录器 (com.normantech.ibcol.radiobox.RReceiver) 的附加程序。
log4j:WARN 请正确初始化 log4j 系统。

警告并不总是出现。我怀疑有错误的取消初始化顺序,但无法弄清楚。为什么会这样?我试过使用 LogManager.shutdown(); 但这没有帮助。我试图很好地完成我的无限循环,但这不是最好的解决方案,因为 II 需要添加一些额外的 Thread.sleep(x),尝试不同的 x,实际上并不确定这是否有帮助。

4

1 回答 1

2

核心问题是你通过调用 System.exit() 来杀死你的线程,而不是让它们干净地关闭。这意味着它们可能会在其他所有东西都关闭之后但在 JVM 实际结束进程之前保持非常短暂的运行,并且如果它们在此期间使用任何共享状态(例如日志记录系统),它可能会处于不一致或损坏的状态,因为它可能已关闭。这不是好的做法(请参阅http://www.javapractices.com/topic/TopicAction.do?Id=86)。

解决这个问题的方法是在关闭共享资源(日志系统)之前正确关闭你的线程。不仅仅是调用 system.exit(),而是告诉每个线程该停止了,并等待它们这样做。最干净的方法是让他们running在每个循环上检查一个布尔值,并在您希望循环停止时在外部将该布尔值设置为 false。然后,您可以等待线程注意到这个布尔值并通过在给定线程实例上调用 Thread.join() 来终止,或者在finished完成时让线程设置一些布尔值(最好将这两个布尔值都标记为 volatile)。

如果您的循环的每次迭代都需要一些时间,或者包含wait()sleep()调用,您应该查看在循环期间更频繁地检查正在运行的布尔值,或者使用Thread.interrupt().

一旦完成并且您确定所有其他线程都已停止,您就可以调用LogManager.shutdown()并实际终止您的进程。

于 2013-04-23T12:27:31.213 回答