在开发调测过程遇到线程非正常阻塞的情况是在所难免的, 有时是死锁, 有时是不正确的程序逻辑. 例如:
package demo; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; public class Main { private static final String DEATH_TAG = "exit"; private static BlockingQueue<String> taskList = new LinkedBlockingQueue<String>(); public static void main(String[] args)throws Exception { ExecutorService es = Executors.newCachedThreadPool(); // 创建一个新线程,使用阻塞队列接收字符串, 如果不放入DEATH_TAG, 则一直进行处理 es.submit(new Runnable() { @Override public void run() { while (true) { String s = null; try { s = taskList.take(); if (DEATH_TAG.equals(s)) { System.out.println("bye"); break; } System.out.println(s); } catch (InterruptedException e) { e.printStackTrace(); } } } }); // taskList.put(DEATH_TAG); es.shutdown(); } }
如果程序结束前没有放入DEATH_TAG, 整个程序都不能正常退出, 就在那里一直阻塞.
出现了线程阻塞, 通常我们会使用jdk自带的jstack或者jconsole命令, 或者eclipse的debug模式, 查看当前线程堆栈情况. 可以看到是线程"pool-1-thread-1"没有退出:
线程stack信息:
"pool-1-thread-1" prio=6 tid=0x0b94fc00 nid=0x1708 waiting on condition [0x0bf1f000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x03d2a980> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1925) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:358) at demo.Main$1.run(Main.java:23) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
但是stack信息只有jdk自带类的方法调用过程, 我们并看不出到底是哪个对象创建了这个线程, 这阻碍了我们的调测.
这时, JProfiler的"Thread Views"功能则发挥了作用. 使用"Thread Views"可以清楚的看到每个线程的创建情况, 从而清楚的看到线程的创建者是"demo.Main":