我遇到了一个非常特殊的问题,除了将问题分成两类之外,我无法解决。

我想知道是否存在不拆分类的解决方案,更重要的是,我想知道是否有人知道Java引擎为什么决定以这种方式行事。

问题:
我有一个带有静态方法,静态字段和构造函数的类。静态字段被初始化为类本身的实例。在实例初始化期间,我想访问前面提到的静态方法。请参见以下代码:

public class Simple {
    public Simple() {
        int count = 4;

        for (int i = 0; i < count; i++) {
            System.out.println("Simple: " + Simple.isFlag());
        }

    }

    private static Simple i = new Simple();

    public static boolean isFlag() {
        return true;
    }

    public static void run() {

    }
}

public class Main {

    public static void main(String[] args) {
        Simple.run();
    }

}

这段代码运行得很好。输出如下所示:
Simple: true
Simple: true
Simple: true
Simple: true

因为我仅在访问该类的第一个静态成员之后才初始化stativ字段,所以在我调用run()方法之后生成了输出。

我现在想做完全相同的事情,除了有多个线程。看这里:
public class Parallel {
    public Parallel() {
        int count = 4;

        CountDownLatch latch = new CountDownLatch(4);
        for (int i = 0; i < count; i++) {
            Thread t = new Thread(() -> {
                System.out.println("Parallel: " + Parallel.isFlag());
                latch.countDown();

                Thread.currentThread().interrupt();
            });

            t.start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private static Parallel i = new Parallel();

    public static boolean isFlag() {
        return true;
    }

    public static void run() {

    }
}

public class Main {

    public static void main(String[] args) {
        Parallel.run();
    }

}

这将返回,不返回。主线程卡在latch.await();上,而其他线程卡在Parallel.isFlag()上。编辑:如下面的Jaims所示,线程甚至根本不会启动。

这对我来说没有任何意义。为什么这不起作用,但第一种情况是?本质上,他们在做同样的事情。

我想知道Java引擎如何决定何时等待和何时不等待。可以在代码中的某处更改吗?

此外,这与CountDownLatch无关,而仅与多线程有关。看一下这个最终样本:
public class NonParallel {
    public NonParallel() {
        int count = 4;

        CountDownLatch latch = new CountDownLatch(4);
        for (int i = 0; i < count; i++) {
            System.out.println("NonParallel: " + NonParallel.isFlag());
            latch.countDown();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static NonParallel i = new NonParallel();

    public static boolean isFlag() {
        return true;
    }

    public static void run() {

    }
}

public class Main {

    public static void main(String[] args) {
        NonParallel.run();
    }

}

这很好。输出如下:
NonParallel: true
NonParallel: true
NonParallel: true
NonParallel: true

编辑:当对象初始化不是类初始化的一部分时,这都不适用。这纯粹是关于类初始化的,仅在使用此问题中所述的静态对象时才会发生。看这里:
public class NonStaticParallel {
    public NonStaticParallel() {
        int count = 4;

        CountDownLatch latch = new CountDownLatch(4);
        for (int i = 0; i < count; i++) {
            Thread t = new Thread(() -> {
                System.out.println("NonStaticParallel: " + isFlag());
                latch.countDown();

            });

            t.start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    public  static  boolean isFlag() {
        return true;
    }

    public static void run() {
        new NonStaticParallel();
    }

}

这一工作原理没有任何问题:
Parallel: true
Parallel: true
Parallel: true
Parallel: true

答案:

安德烈亚斯(Andreas)提供了有关正在发生的事情的解释。

Jaims是对的,因为线程根本不会启动。发生这种情况的原因可能是,它们需要初始化该类,因此会立即将其阻塞。 (如果我们使用属于自己的类而不是lambda或匿名内部类的可运行对象,那么它们将正常运行,除非他们当然要求初始化了该类的任何静态成员)

Yoshi提供了规范的链接和摘录,因此被标记为正确的答案,因为这正是我想要的。

最佳答案

我尝试了您的代码,做了两件事:

  • 首先,我将lambda设置为Parallel的静态内部类,以防万一;这并没有改变任何东西。
  • 既然您评论说线程卡在了Parallel.isFlag()上,所以我尝试仅用true替换该调用...并且它起作用了!

  • 因此,我做了一些研究,发现了这一点,这听起来像是对正在发生的事情的有希望的解释:http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2

    特别是这部分:



    (添加了强调。)因此,这将建议以下内容:
  • 主线程在评估private static Parallel i = new Parallel();时启动了类初始化,并启动了线程。然后,它等待latch.await()Parallel的类对象应指示初始化“正在进行中”。
  • 启动的线程还尝试引用Parallel的静态成员。每个线程都看到初始化正在进行中,并决定等待直到主线程(现在正在等待线程以递减锁存器)完成。显然,这是一个僵局。
  • 10-07 18:54
    查看更多