在IDE中使用IntelliJ IDE,java 1.8,lang level 6(以及命令提示符下的默认值)。
使用以下命令从命令行(Windows 7)进行编译和运行
javac -cp . Main.java
java -cp . Main
鉴于以下代码,我想要做的是关闭应用程序中所有正在运行的线程(如果操作系统或JVM为我做的话,我可能不需要这样做)。我有一个线程MyThread,它反过来拥有一个它正在管理的线程列表(当被主节点中断时,它会向其列表中的每个线程发送一个中断)。
当我运行代码时,我每2秒打印一次输出Keeping busy.
。当我按下CTRL-C时,关闭钩子运行(我把println消息放在那里确认)但这就是我看到的所有输出。我从来没有看到Cancelled.
nr是main()的结尾......所以我还必须假设Cancel方法也没有被运行。
所以,首先,我需要关心这个吗?我是否可以在isInterrupted上循环(在main中)并让程序停止?如何取消所有子线程?
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);
}
}
修改:
所以,我改变了我的代码如下。创建了一个包含hashmap的Threadlist类,其中的方法使用关键字'synchronized'来添加/删除线程并取消所有线程。 main包含此类的静态实例。我的关闭处理程序然后调用CancelThreads方法来关闭所有正在运行的线程。问题:这是真正的线程安全吗,即:是否正在同步实际阻止一个方法在另一个方法正在进行时运行的方法?
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);
}
}
我还开始创建一个ShutdownHook类,扩展Thread,它会通过构造函数接受main的线程对象,然后取消run()方法中的线程,但是我看不到如何更改main-&gt ;同时运行价值,因此,我的方法在上面。
答案 0 :(得分:4)
你的关闭钩子是一个单独的线程,在关闭时启动(希望,不能保证)。与此同时,你的主线仍在进步,但它即将被杀。这可能是有效的,但是在你的代码杀死主线程之前,中断可能不会给你2秒的代码。
相反,您应该使用shutdown hook共享生成的线程列表,并让该钩子尝试直接中断/关闭生成的线程。
答案 1 :(得分:2)
一旦关闭钩子完成运行,VM就会停止运行(实际上还有另一个步骤,但与此讨论无关)。
这里的关键是你的关闭钩子需要活得足够长,以便其他线程可以清理,你可以通过在另一个线程上使用关闭钩子调用Thread.join(long)
并超时来实现。这将允许关闭挂钩在另一个线程之后或超过指定的超时时结束。
通常认为阻止关机进程不是用户友好的。您的关闭挂钩应该在加入之前唤醒或发出其他线程的信号,如何完成此操作取决于您(interrupt()
,wait()
/ notifyAll()
配对或使用来自{的类{1}}包。
示例:
java.util.concurrent
上面的例子存在一些问题,一个很大的问题是 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);
}
}
作为计时器没有用(wait()
被允许虚假唤醒),所以如果你真的需要它每2秒逻辑就更复杂了。但是,这概述了让它发挥作用的基础。