1
"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);
        }
4

2 回答 2

2

这可能表明存在编码问题。代码等待FutureTask尚未执行的 a 的完成。

在下面找到演示片段

未来.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的PIDjava Future

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(..)这两个任务后,我们立即等待第二个任务的结果。由于第一项任务是一项长期运行的任务,我们将进入您发现的情况。

未来.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-1- 是future1当前休眠的转储(模拟长时间运行的任务)

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

于 2016-04-22T09:03:33.187 回答
2

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

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

简而言之,这意味着有问题的线程已经调用Object.wait()了相关监视器上的方法。

Java 并发支持的核心特性之一是调用的低级构造wait/notify,它被用作某种用于线程间通信的工具。当线程使用共享可变状态时,在线程安全程序中,它们应该确保该状态在对其进行操作之前满足某些条件(状态)。

经典的生产者-消费者模式中的一个例子是消费者线程等待某个共享队列至少有一个要消费的项目。因此,对于消费者线程做任何有意义的工作,它依赖于其他(例如生产者)线程来生产项目。为了使其正常工作,消费者线程进入队列中的监视器(进入临界区,持有排他锁),然后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是什么)。

于 2016-04-23T01:41:08.603 回答