I work on a big Java service that does lots of things. When I snapshot the application’s threads I can see it’s got lots going on:
- Talking RPC and HTTP
- Running cron jobs and job queue jobs
- Persisting to MySQL
- Coordinating with ZooKeeper
- Collecting garbage
At a glance I can see what the service is doing: what’s blocked on I/O, what’s executing, what’s waiting for locks, and what’s idle.
Analysis is easier if the threads have descriptive names:
connection-mgmt-[twilio.com:443]
job-queue-outgoing-email-0
zk-lb-lease/publisher-outgoing_sms-0
And it’s difficult otherwise:
pool-1-thread-3
pool-21-thread-99
pool-2-thread-1
Naming Threads
The best names help you to know out which class started the thread (connection-mgmt
) and what that thread is currently doing (twilio.com:443
).
If you’re using Guava, just use ThreadFactoryBuilder. Otherwise use this:
public static ThreadFactory threadFactory(
final String name, final boolean daemon) {
return new ThreadFactory() {
@Override public Thread newThread(Runnable runnable) {
Thread result = new Thread(runnable, name);
result.setDaemon(daemon);
return result;
}
};
}
When your runnable has more context than it’s shared executor, you can use something like OkHttp’s NamedRunnable:
executor.execute(new NamedRunnable("OkHttp Window Update %s", hostname) {
@Override public void execute() {
writer.windowUpdate(streamId, unacknowledgedBytesRead);
}
});
Seeing your threads with jstack
The payoff comes when you run jstack
. It’s a JDK program that’s sitting the same bin/
directory as java
and javac
. It will show you the threads of any of the Java programs you’re running.
For example, I can see IntelliJ’s threads by getting its process ID from ps
and then passing that to jstack
.
$ ps aux | grep idea
jwilson 25395 0.0 11.5 10863464 1922108 ?? S 1:11PM 82:09.53 /Applications/IntelliJ IDEA CE.app/Contents/MacOS/idea
...
$ jstack 25395
2017-02-04 12:56:19
Full thread dump OpenJDK 64-Bit Server VM (25.112-b6 mixed mode):
"ApplicationImpl pooled thread 311" #2168 prio=4 os_prio=31 tid=0x00007fe035058000 nid=0x1a2d7 waiting on condition [0x000070000ffb3000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
...
"Attach Listener" #2167 daemon prio=9 os_prio=31 tid=0x00007fe02b7bf800 nid=0x1e687 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"process reaper" #2126 daemon prio=10 os_prio=31 tid=0x00007fe03021c800 nid=0x1d957 runnable [0x0000700012441000]
java.lang.Thread.State: RUNNABLE
at java.lang.UNIXProcess.waitForProcessExit(Native Method)
...
Make Debugging Easier
If you’re creating threads, give ’em names. It’ll make it easier to understand what’s going on in development and to diagnose problems in production.