Skip to content

Instantly share code, notes, and snippets.

@geminicaprograms
Last active May 25, 2021 07:55
Show Gist options
  • Save geminicaprograms/a8b8e0bfe954782866396ee11c7c3967 to your computer and use it in GitHub Desktop.
Save geminicaprograms/a8b8e0bfe954782866396ee11c7c3967 to your computer and use it in GitHub Desktop.
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class Threads {
private static final Object lockObject = new Object();
private static final Random rand = new Random();
private static final AtomicInteger finishedThreads = new AtomicInteger(0);
private static final List<Long> results = new ArrayList<>(200);
private static class BlockedThreadsMetric extends Thread {
@Override
public void run() {
while (finishedThreads.get() < 200) {
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
long startTime = System.currentTimeMillis();
long blocked = 0L;
ThreadInfo[] threads = null;
try {
ThreadMXBean threadsBean = ManagementFactory.getThreadMXBean();
threads =
threadsBean.dumpAllThreads(
threadsBean.isObjectMonitorUsageSupported(),
threadsBean.isSynchronizerUsageSupported());
if (threads == null) {
System.out.println("There are no threads started");
return;
}
blocked =
Arrays.stream(threads)
.filter(
t -> t.getLockInfo() != null && Thread.State.BLOCKED == t.getThreadState())
.count();
// measure deadlocks finding
// long[] ids =
// threadsBean.isSynchronizerUsageSupported()
// ? threadsBean.findDeadlockedThreads()
// : threadsBean.findDeadlockedThreads();
// blocked = ids != null ? ids.length : 0L;
//
// optimised thread dump that doesn't look up for stach trace
// threads = threadsBean.getThreadInfo(threadsBean.getAllThreadIds(), 0);
} finally {
long measurement = System.currentTimeMillis() - startTime;
results.add(measurement);
System.out.println(
String.format(
"Threads %d, blocked threads %d, measurement took: %s ms",
threads != null ? threads.length : 0, blocked, measurement));
}
}
System.out.println(String.format("Total results: %d", results.size()));
Collections.sort(results);
System.out.println(String.format("min: %d", results.get(0)));
System.out.println(String.format("50: %d", percentile(results, 50)));
System.out.println(String.format("90: %d", percentile(results, 90)));
System.out.println(String.format("95: %d", percentile(results, 95)));
System.out.println(String.format("99: %d", percentile(results, 99)));
System.out.println(String.format("max: %d", results.get(results.size() - 1)));
}
}
private static class WorkThread extends Thread {
@Override
public void run() {
try {
synchronized (lockObject) {
Thread.sleep(finishedThreads.get() < 250 ? (rand.nextInt(200) + 100) : 0);
finishedThreads.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
}
public static void main(String[] args) {
int totalThreads = args.length == 1 ? Integer.valueOf(args[0]) : 1000;
BlockedThreadsMetric m = new BlockedThreadsMetric();
m.setName("M");
m.start();
for (int i = 0; i < totalThreads; i++) {
WorkThread t = new WorkThread();
t.setName(String.format("W-%d", i));
t.start();
}
System.out.println(String.format("Started all %d threads", totalThreads));
}
private static long percentile(List<Long> latencies, double percentile) {
int index = (int) Math.ceil(percentile / 100.0 * latencies.size());
return latencies.get(index - 1);
}
}
@geminicaprograms
Copy link
Author

geminicaprograms commented Apr 25, 2021

Once it is compiled one calls it with java -cp ./bin Threads XXXX where XXXX is the number of threads to be started (by default if no argument given 1000 threads are started).

Here are some stats (in millis) that I have generated on my laptop for getting blocked threads depending on the number of threads and Java version (each Java version runs against 1000 and 4000 threads correspondingly).

Blocked v1 Java 8 (1.8.0_282) - 1000 Java 8 (1.8.0_282) - 4000 Java 11 (11.0.10) - 1000 Java 11 (11.0.10) - 4000 Java 15 (15.0.2) - 1000 Java 15 (15.0.2) - 4000
Min 17 156 19 109 5 22
50 29 490 30 230 12 39
90 34 521 37 261 18 67
95 34 527 39 267 19 71
99 36 675 46 276 22 77

For comparison the same stats for the existing deadlocks finding (code is commented in the above snippet)

Deadlocked Java 8 (1.8.0_282) - 1000 Java 8 (1.8.0_282) - 4000 Java 11 (11.0.10) - 1000 Java 11 (11.0.10) - 4000 Java 15 (15.0.2) - 1000 Java 15 (15.0.2) - 4000
Min 0 136 1 7 1 5
50 1 146 2 11 1 7
90 2 152 3 12 2 8
95 2 154 3 12 2 8
99 4 156 12 13 3 8

And finally the optimised version of thread dump that doesn't exercise the stack trace (also in commented part of the snipped)

Blocked v2 Java 8 (1.8.0_282) - 1000 Java 8 (1.8.0_282) - 4000 Java 11 (11.0.10) - 1000 Java 11 (11.0.10) - 4000 Java 15 (15.0.2) - 1000 Java 15 (15.0.2) - 4000
Min 7 78 1 9 1 6
50 10 285 3 12 2 8
90 12 293 3 12 3 9
95 13 303 4 13 3 9
99 15 325 4 27 7 17

And here is the chart comparison (left 5 chart bars for each category are results of deadlocked, middle 5 are results of blocked v1 and right 5 are for blocked v2 threads call):

deadlocked_vs_blockedv1_vs_blockedv2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment