使用Streams API进行奇怪的死锁

时间:2015-04-15 00:25:56

标签: java java-8 java-stream

我正在编写一个程序,并决定使用新的Streams API for Java 8.但是,当我介绍.parallel()时,我的程序停止了工作。这是相关的代码:

import java.math.BigInteger;
import java.util.Objects;
import java.util.stream.Stream;

import com.google.common.cache.*;

public class Alg196 {

    public static void main(String[] args) {
        // Add .parallel() where suitable
        long c = Stream
                .iterate(BigInteger.valueOf(101), i -> i.add(BigInteger.ONE))
                .limit(100000000).map(BigInteger::toString).map(Alg196::alg196)
                .filter(Objects::nonNull).count();
        System.err.println(c);
    }

    private static final String reverse(String n) {
        return new StringBuilder(n).reverse().toString();
    }

    private static final boolean isPalindrome(String s) {
        for (int i = 0, j = s.length() - 1; i < j; ++i, --j) {
            if (s.charAt(i) != s.charAt(j))
                return false;
        }
        return true;
    }

    private static final String alg196(String n) {
        System.err.println("PROCESSING " + n);
        int loops = 0;
        while (!isPalindrome(n)) {
            n = new BigInteger(n).add(new BigInteger(reverse(n))).toString();
            loops++;
            if (loops >= 100) {
                return null;
            }
        }
        if (loops <= 10) {
            return null;
        }
        return n;
    }
}

正常工作时,输出将包含许多PROCESSING <x>行,但.parallel()不会发生这种情况。这是为什么?

2 个答案:

答案 0 :(得分:2)

你的程序没有停止 - 在向执行程序提交map任务之前尝试生成请求的BigIntegers范围(在你的情况下是100000000)很努力(尝试将断点放在BigInteger :: add()方法 - 而你我会看到的。

从线程转储中也很容易看到

"ForkJoinPool.commonPool-worker-2@710" daemon prio=5 tid=0xe nid=NA runnable java.lang.Thread.State: RUNNABLE
at java.util.stream.Stream$1.next(Stream.java:1033)
  at java.util.Spliterators$IteratorSpliterator.trySplit(Spliterators.java:1784)
  at java.util.stream.AbstractShortCircuitTask.compute(AbstractShortCircuitTask.java:114)
  at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
  at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
  at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902)
  at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689)
  at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644)
  at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)`

另外请注意将许多长时间运行的任务提交到公共ForkJoin池,因为您可能会阻塞池中的所有线程 - 您可以检查此线程(Custom thread pool in Java 8 parallel stream)以获取解决方案

答案 1 :(得分:0)

事实证明limit是一个短路操作,这意味着它会在运行任何其他内容之前生成所有100,000,000 BigIntegers。毕竟不是死锁!