繁体   English   中英

分析Java线程转储

[英]Analyzing the java thread dumps

"Star Builder 129" daemon prio=10 tid=0x00007f41bd8f6000 nid=0x152b0 waiting on condition [0x00007f445cd1a000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00007f59c9e1c278> (a java.util.concurrent.FutureTask)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
        at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:422)
        at java.util.concurrent.FutureTask.get(FutureTask.java:199)

我花了最后一天试图了解这实际上意味着什么。 没有足够的信息,或者我找不到任何有用的信息。

“等待条件”是什么意思? 我们在等待监视器吗? “ 0x00007f445cd1a000”表示什么?

“停车等待”是什么意思? 什么是“ 0x00007f59c9e1c278”?

源代码:

        List<FutureTask<List<FileStatus>>> tasks = new LinkedList<FutureTask<List<FileStatus>>>();
    for(int idx = 0, len = Math.max(1,numberOfThreads()); idx < len; ++idx) {
        StatusWorker worker = new StatusWorker(this, qualifiedPath, userFilter, prefixQueue, prefixCounter, statusCounter);
        FutureTask<List<FileStatus>> task = new FutureTask<List<FileStatus>>(worker);
        threadPool.execute(task);
        tasks.add(task);
    }

    try {
        List<FileStatus> statuses = new LinkedList<FileStatus>();

        for(FutureTask<List<FileStatus>> task : tasks) {
            statuses.addAll(task.get(FILE_LISTING_TIMEOUT, TimeUnit.SECONDS));
            logger.debug("Result from task [{}]", task);
        }

这可能指向编码问题。 该代码等待尚未执行的FutureTask的完成。

在下面的代码段中进行演示

Future.java

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class Future {

    public static void main(String[] args) throws Exception {
        FutureTask<String> future = new FutureTask<>(
                new Callable<String>() {
                    @Override
                    public String call() throws InterruptedException {
                        return "foo";
                    }
                });
        String get = future.get(30, TimeUnit.SECONDS);
        System.out.println("get = " + get);
    }
}

在会话1中运行

javac Future.java
java Future

在会话2中运行

$ jps
...
12345 Future
jstack -l 12345 > jstack.12345.log

注意: 12345是正在运行的java Future进程的PID

jstack.12345.log的内容

    "main" #1 prio=5 os_prio=0 tid=0x000000000273b000 nid=0x2b24 waiting on condition [0x0000000002abf000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000781973910> (a java.util.concurrent.FutureTask)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:426)
        at java.util.concurrent.FutureTask.get(FutureTask.java:204)
        at Future.main(Future.java:19)

编辑根据发布的代码段,描述的情况很容易发生。 引用Executor.execute的javadoc

在将来的某个时间执行给定命令。

at some time in the future这意味着不是立即调用execute 因此,您可能在实际执行任务之前先到达task.get(...)行。 您的线程池也可能无法同时运行numberOfThreads()线程。

一个小示例进行演示(运行与前面所述相同的命令)。 我们创建一个线程池,该线程池一次仅执行一个任务。 我们向该池分配两个任务。 在为两个任务调用executor.execute(..) ,我们立即等待第二个任务的结果。 由于第一个任务是长期运行的任务,因此我们进入您发现的情况。

Future.java

public class Future {

    public static void main(String[] args) throws Exception {
        FutureTask<String> future1 = new FutureTask<>(
                () -> {
                    System.out.println("future1");
                    TimeUnit.SECONDS.sleep(50);
                    return "finished future1";
                });

        FutureTask<String> future2 = new FutureTask<>(
                () -> {
                    System.out.println("future2");
                    return "finished future2";
        });

        ExecutorService executor = Executors.newFixedThreadPool(1);
        executor.execute(future1);
        executor.execute(future2);

        String get = future2.get(30, TimeUnit.SECONDS);
    }
}

jstack.12345.log的内容

"pool-1-thread-1" #9 prio=5 os_prio=0 tid=0x00007ff50811c000 nid=0x5a5c waiting on condition [0x00007ff4e7365000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at concurrent.Future$1.call(Future.java:19)
    at concurrent.Future$1.call(Future.java:15)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"main" #1 prio=5 os_prio=0 tid=0x00007ff50800a000 nid=0x5a4d waiting on condition [0x00007ff50e314000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000ec03dda8> (a java.util.concurrent.FutureTask)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:426)
    at java.util.concurrent.FutureTask.get(FutureTask.java:204)
    at concurrent.Future.main(Future.java:36)

pool-1-thread-1future1的转储,当前正在休眠(以模拟长时间运行的任务)

main -是转储future2.get(30, TimeUnit.SECONDS)它等待的结果future2 ,这实际上是尚未开始。

应该更好地记录Oracle Hotspot JVM上的线程转储(一种出色的调试工具),以便对这些问题的答案有一些了解,但是这些基本知识是基于Java对多个线程的内置支持。 这是我对您的问题的看法:

“等待条件”是什么意思?

简而言之,这意味着所讨论的线程已在关联的监视器上调用了Object.wait()方法。

Java并发支持的主要功能之一是称为wait/notify的低级构造,它用作某种类型的线程间通信工具。 当线程使用共享的可变状态时 ,在线程安全的程序中,它们应确保该状态满足某些条件,然后再对其进行操作(状态)。

经典的Producer-Consumer模式中的一个示例是,Consumer线程等待某个共享队列中至少要消耗一个项目。 因此,对于消费者线程执行任何有意义的工作,它依赖于其他(例如生产者)线程来产生项目。 为了使其正常工作,消费者线程在队列上进入监视器(进入临界区,持有排他锁),但随后在某些条件为真while旋转(应该有一个while循环)。

如果条件为假,它将立即放弃对监视器的锁定(退出监视器),并要求在下次JVM选择线程进入同一监视器时以某种方式牢记该锁。 您会看到,不放弃锁,就不会有任何进展。 Java 通过与监视器关联的等待集的概念来实现此目的

我们在等待监视器吗?

不能。等待,或者等待进入监视器将阻塞正在等待的线程。 这样的(阻塞的)线程的线程转储代码片段如下所示:

"thread2" #12 prio=5 os_prio=31 tid=0x00007ff51607d000 nid=0x5903 waiting for monitor entry [0x000000012f693000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at juc.SyncRace$Lock.sleep(SyncRace.java:11)
    - waiting to lock <0x000000076b021630> (a java.lang.Class for juc.SyncRace$Lock)

在此示例转储中,线程thread2希望获取某个其他线程当前持有的锁(在线程转储时)。 这使thread2进入BLOCKED状态

“ 0x00007f445cd1a000”表示什么?

它指示线程正在等待变为真的条件变量

“停车等待”是什么意思? 什么是“ 0x00007f59c9e1c278”?

等待条件变量变为真的所有线程在线程转储中都有一个非常相似的片段。 实际的等待支持是通过LockSupport.park()Unsafe.park (我不确定0x00007f59c9e1c278是什么)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM