如果我在并行流中使用lambda会发生死锁,但如果我使用匿名类而不会发生这种情况? [英] Deadlock happens if I use lambda in parallel stream but it doesn't happen if I use anonymous class instead?

查看:136
本文介绍了如果我在并行流中使用lambda会发生死锁,但如果我使用匿名类而不会发生这种情况?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码导致死锁(在我的电脑上):

  public class Test {
static {
final int SUM = IntStream.range(0,100)
.parallel()
.reduce((n,m) - > n + m)
.getAsInt() ;
}

public static void main(String [] args){
System.out.println(Finished);
}
}

但如果我更换减少带有匿名类的lambda参数不会导致死锁:

  public class Test {
static {
final int SUM = IntStream.range(0,100)
.parallel()
.reduce(new IntBinaryOperator(){
@Override
public int applyAsInt(int n,int m){
return n + m;
}
})
.getAsInt();
}

public static void main(String [] args){
System.out.println(Finished);
}
}

你能解释一下这种情况吗?



PS



我发现该代码(与之前略有不同):

  public class Test {
static {
final int SUM = IntStream.range(0,100)
.parallel()
.reduce(new IntBinaryOperator(){
@Override
public int applyAsInt(int n,int m){
return sum(n,m);
}
})
.getAsInt();
}

private static int sum(int n,int m){
return n + m;
}

public static void main(String [] args){
System.out.println(Finished);
}
}

工作不稳定。在大多数情况下,它会挂起但有时它会成功完成:





我真的无法理解为什么这种行为不稳定。实际上我重新测试第一个代码片段并且行为相同。所以最新的代码等于第一个。



要了解使用了哪些线程,我在logging之后添加:

  public class Test {
static {
final int SUM = IntStream.range(0,100)
.parallel()
.reduce((n,m) - > {
System.out.println(Thread.currentThread()。getName());
return(n + m);
})
.getAsInt();
}

public static void main(String [] args){
System.out.println(Finished);
}
}

如果申请成功完成,我会看到以下日志:

  main 
main
main
main
ForkJoinPool.commonPool-worker- 1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool .commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool。 commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.c ommonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool -worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool- worker-1
ForkJoinPool.commonPool-worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool -worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool .commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
已售完



PS 2



我认为减少是足够复杂的操作。我找到了一个更简单的例子来证明这个问题:

  public class Test {
static {
System。 out.println(static initializer:+ Thread.currentThread()。getName());

final long SUM = IntStream.range(0,2)
.parallel()
.mapToObj(i - > {
System.out.println( map:+ Thread.currentThread()。getName()++ i);
return i;
})
.count();
}

public static void main(String [] args){
System.out.println(Finished);
}
}

快乐案例(罕见情况)我看到以下输出:

 静态初始化程序:main 
map:main 1
map:main 0
已完成

扩展流范围的快乐案例示例:

 静态初始化程序:main 
map:main 2
map:main 3
map:ForkJoinPool.commonPool-worker-2 4
map:ForkJoinPool.commonPool-worker-1 1
map:ForkJoinPool.commonPool-worker-3 0
已完成

导致死锁的案例:

 静态初始化程序:main 
map: main 1

它也会导致死锁,但每次启动都不会。

解决方案

区别在于lambda body是用相同的 Test 类编写的,即合成方法

  private static int lambda $ static $ 0(int n,int m){
ret urn n + m;
}

在第二种情况下,接口的实现位于不同的 测试$ 1 类。因此,并行流的线程不会调用 Test 的静态方法,因此不依赖于 Test 初始化。 / p>

The following code leads to deadlock(on my pc):

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce((n, m) -> n + m)
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

But if I replace reduce lambda argument with anonymous class it doesn't lead to deadlock:

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    @Override
                    public int applyAsInt(int n, int m) {
                        return n + m;
                    }
                })
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

Could you explain that situation?

P.S.

I found that code(a bit different from previous):

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    @Override
                    public int applyAsInt(int n, int m) {
                        return sum(n, m);
                    }
                })
                .getAsInt();
    }

    private static int sum(int n, int m) {
        return n + m;
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

works not stable. In most cases it hangs buts sometimes it finishes successfully:

I really not able to understand why this behaviour is not stable. Actually I retest first code snippet and behaviour the same. So the latest code is equals the first one.

To understand which threads are used I added following "logging":

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce((n, m) -> {
                    System.out.println(Thread.currentThread().getName());
                    return (n + m);
                })
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

For case when application finishes successfully I see following log:

main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
Finished

P.S. 2

I Undestand that reduce is enough complex operations. I found a simpler example to show that problem:

public class Test {
    static {
        System.out.println("static initializer: " + Thread.currentThread().getName());

        final long SUM = IntStream.range(0, 2)
                .parallel()
                .mapToObj(i -> {
                    System.out.println("map: " + Thread.currentThread().getName() + " " + i);
                    return i;
                })
                .count();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

for happy case(rare case) I seee following output:

static initializer: main
map: main 1
map: main 0
Finished

example of happy case for extended stream range:

static initializer: main
map: main 2
map: main 3
map: ForkJoinPool.commonPool-worker-2 4
map: ForkJoinPool.commonPool-worker-1 1
map: ForkJoinPool.commonPool-worker-3 0
Finished

example of case which leads to deadlock:

static initializer: main
map: main 1

It also leads to deadlock but not for each start.

解决方案

The difference is that lambda body is written in the same Test class, i.e. a synthetic method

private static int lambda$static$0(int n, int m) {
    return n + m;
}

In the second case the implementation of the interface resides in a different Test$1 class. So the threads of a parallel stream do not call static methods of Test and thus do not depend on Test initialization.

这篇关于如果我在并行流中使用lambda会发生死锁,但如果我使用匿名类而不会发生这种情况?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆