Java通过线程名kill掉线程

背景

在生产环境,有时候因为代码bug或者数据量太大,导致某个线程死循环或者耗时非常长线程hang住了。在一些场景下,我们希望可以在不重启服务的情况下把这些线程终止了。

在 ActiveMQ 源码中发现一个通过线程名stop线程的方法,在此分享出来供参考。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;

public class ThreadExplorer
{
static Logger logger = Logger.getLogger(ThreadExplorer.class);

public static Thread[] listThreads()
{

int nThreads = Thread.activeCount();
Thread ret[] = new Thread[nThreads];

Thread.enumerate(ret);

return ret;

}

/**
* Helper function to access a thread per name (ignoring case)
*
* @param name
* @return
*/
public static Thread fetchThread(String name)
{
Thread[] threadArray = listThreads();
// for (Thread t : threadArray)
for (int i = 0; i < threadArray.length; i++)
{
Thread t = threadArray[i];
if (t.getName().equalsIgnoreCase(name))
return t;
}
return null;
}

/**
* Allow for killing threads
*
* @param threadName
* @param isStarredExp
* (regular expressions with *)
*/
@SuppressWarnings("deprecation")
public static int kill(String threadName, boolean isStarredExp)
{
String me = "ThreadExplorer.kill: ";
if (logger.isDebugEnabled())
{
logger.debug("Entering " + me + " with " + threadName + " isStarred: " + isStarredExp);
}
int ret = 0;
Pattern mypattern = null;
if (isStarredExp)
{
String realreg = threadName.toLowerCase().replaceAll("\\*", "\\.\\*");
mypattern = Pattern.compile(realreg);

}
Thread[] threads = listThreads();
for (int i = 0; i < threads.length; i++)
{
Thread thread = threads[i];
if (thread == null)
continue;
// kill the thread unless it is not current thread
boolean matches = false;

if (isStarredExp)
{
Matcher matcher = mypattern.matcher(thread.getName().toLowerCase());
matches = matcher.matches();
}
else
{
matches = (thread.getName().equalsIgnoreCase(threadName));
}
if (matches && (Thread.currentThread() != thread) && !thread.getName().equals("main"))
{
if (logger.isInfoEnabled())
logger.info("Killing thread named [" + thread.getName() + "]"); // , removing its uncaught

ret++;

try
{
thread.stop();
}
catch (ThreadDeath e)
{
logger.warn("Thread already death.", e);
}

}
}
return ret;
}

public static String show(String title)
{
StringBuffer out = new StringBuffer();
Thread[] threadArray = ThreadExplorer.listThreads();

out.append(title + "\n");
for (int i = 0; i < threadArray.length; i++)
{
Thread thread = threadArray[i];

if (thread != null)
{
out.append("* [" + thread.getName() + "] " + (thread.isDaemon() ? "(Daemon)" : "")
+ " Group: " + thread.getThreadGroup().getName() + "\n");
}
else
{
out.append("* ThreadDeath: " + thread + "\n");
}

}
return out.toString();
}

public static int active()
{
int count = 0;
Thread[] threadArray = ThreadExplorer.listThreads();

for (int i = 0; i < threadArray.length; i++)
{
Thread thread = threadArray[i];
if (thread != null)
{
count++;
}
}

return count;
}

}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestMain {


public static void main(String[] args) throws IOException, InterruptedException {

ExecutorService threadPool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) { //10个线程死循环
int finalI = i;
threadPool.execute(() -> {
for (;;){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("中断Thread#=="+ finalI);
e.printStackTrace();
}
}
});
}
threadPool.shutdown();

System.out.println(ThreadExplorer.show("Kill thread 开始前线程#####"));
ThreadExplorer.kill("pool-1-thread-1", false, "");
ThreadExplorer.kill("pool-1-thread-3", false, "");
//ThreadExplorer.kill("pool-1-thread-*", truer, "");//stop所有线程名符合pool-1-thread-*的线程

Thread.sleep(1000);// 休眠只是为了后面能够打印的时候准确显示

System.out.println(ThreadExplorer.show("Kill thread 结束后剩余线程#####"));
}
}

示例结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Kill thread 开始前线程#####
* [main] Group: main
* [pool-1-thread-1] Group: main
* [pool-1-thread-2] Group: main
* [pool-1-thread-3] Group: main
* [pool-1-thread-4] Group: main
* [pool-1-thread-5] Group: main
* [pool-1-thread-6] Group: main
* [pool-1-thread-7] Group: main
* [pool-1-thread-8] Group: main
* [pool-1-thread-9] Group: main
* [pool-1-thread-10] Group: main

Killing thread named [pool-1-thread-1]
Killing thread named [pool-1-thread-3]
Kill thread 结束后剩余线程#####
* [main] Group: main
* [pool-1-thread-2] Group: main
* [pool-1-thread-4] Group: main
* [pool-1-thread-5] Group: main
* [pool-1-thread-6] Group: main
* [pool-1-thread-7] Group: main
* [pool-1-thread-8] Group: main
* [pool-1-thread-9] Group: main
* [pool-1-thread-10] Group: main

从结果可以看到,线程pool-1-thread-1 和 pool-1-thread-3 已经被干掉了。

作者

csc101

发布于

2022-03-19

更新于

2022-03-19

许可协议