我遇到了一个非常特殊的问题,除了将问题分成两类之外,我无法解决。
我想知道是否存在不拆分类的解决方案,更重要的是,我想知道是否有人知道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提供了规范的链接和摘录,因此被标记为正确的答案,因为这正是我想要的。
最佳答案
我尝试了您的代码,做了两件事:
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
的静态成员。每个线程都看到初始化正在进行中,并决定等待直到主线程(现在正在等待线程以递减锁存器)完成。显然,这是一个僵局。