Java 从另一个线程初始化同一类的静态字段时访问静态方法
我遇到了一个非常奇怪的问题,除了把问题分成两类之外,我无法解决这个问题 我想知道是否有一个不拆分类的解决方案,更重要的是,我想知道是否有人知道为什么Java引擎决定这样做 问题是: 我有一个带有静态方法、静态字段和构造函数的类。静态字段初始化为类本身的实例。在实例初始化期间,我想访问前面提到的静态方法。请参阅以下代码:Java 从另一个线程初始化同一类的静态字段时访问静态方法,java,multithreading,static,static-initialization,Java,Multithreading,Static,Static Initialization,我遇到了一个非常奇怪的问题,除了把问题分成两类之外,我无法解决这个问题 我想知道是否有一个不拆分类的解决方案,更重要的是,我想知道是否有人知道为什么Java引擎决定这样做 问题是: 我有一个带有静态方法、静态字段和构造函数的类。静态字段初始化为类本身的实例。在实例初始化期间,我想访问前面提到的静态方法。请参阅以下代码: public class Simple { public Simple() { int count = 4; for (int i =
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();
}
}
输出是在调用run()
方法后生成的,因为stativ字段I仅在访问该类的第一个静态成员后初始化
我现在想做完全相同的事情,除了多个线程。请看这里:
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();
}
}
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();
}
}
这个很好用。输出如下:
NonParallel: true
NonParallel: true
NonParallel: true
NonParallel: true
编辑:当对象初始化不是类初始化的一部分时,所有这些都不适用。这纯粹是关于类初始化的,只有在使用本问题中描述的静态对象时才会发生。请看这里:
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();
}
}
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();
}
}
答复: 安德烈亚斯解释了正在发生的事情 Jaims的观点是正确的,线程甚至根本没有启动。这可能是因为它们需要初始化类,因此会立即被阻止(如果我们使用自己类中的可运行项而不是lambda或匿名内部类,那么它们会正常运行,当然除非它们访问正在初始化的类的任何静态成员)
Yoshi提供了一个链接和规范的摘录,因此被标记为正确答案,因为这是我想要的 在正确创建对象之前,不会启动线程。考虑下面的片段:
public class Main {
public static void main(String[] args) {
Parallel.run();
}
}
class Parallel {
private static Parallel i = new Parallel();
public Parallel() {
try {
System.out.println("Inside constructor.");
for (int i = 0; i < 4; i++) {
Thread t = new Thread(() -> {
System.out.println("Running thread.");
});
System.out.println("Starting thread.");
t.start();
}
System.out.println("Sleeping 2 seconds.");
Thread.sleep(2000);
System.out.println("Leaving constructor.");
} catch (InterruptedException ex) {
Logger.getLogger(Parallel.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void run() {
}
}
如输出所示,线程在构造函数中启动4次。它开始休眠2秒,离开构造函数,然后运行线程。不像线程运行需要2秒钟
因此,您的问题的核心问题是,您正在调用latch.await()
,但您的线程从未获得实际运行的机会。这意味着闩锁不会减少,只是保持等待。您可以将逻辑移到run()
方法中,但我不确定首先要实现什么。e、 g
public static void run() {
int count = 4;
CountDownLatch latch = new CountDownLatch(4);
for (int i = 0; i < count; i++) {
Thread t = new Thread(() -> {
try {
Thread.sleep(2000);
latch.countDown();
} catch (InterruptedException ex) {
Logger.getLogger(Parallel.class.getName()).log(Level.SEVERE, null, ex);
}
});
System.out.println("Starting thread.");
t.start();
}
try {
System.out.println("Current count: " + latch.getCount());
latch.await();
System.out.println("Current count: " + latch.getCount());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
publicstaticvoidrun(){
整数计数=4;
倒计时闩锁=新倒计时闩锁(4);
for(int i=0;i{
试一试{
《睡眠》(2000年);
倒计时();
}捕获(中断异常例外){
Logger.getLogger(Parallel.class.getName()).log(Level.SEVERE,null,ex);
}
});
System.out.println(“起始线程”);
t、 start();
}
试一试{
System.out.println(“当前计数:+latch.getCount());
satch.wait();
System.out.println(“当前计数:+latch.getCount());
}捕捉(中断异常e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
调用run()
时,当前线程将开始类初始化。任何引用类的代码,例如调用isFlag()
,都需要初始化类
在Simple
和NonParallel
版本中,当前线程正在执行所有操作,并且允许递归类初始化(实际上被忽略),因此执行isFlag()
,即使类初始化尚未完成
但是,在您的Parallel
版本中,对isFlag()
的调用是从另一个线程完成的,因此其他线程必须等待类完全初始化。由于构造函数在线程运行之前不会返回,并且在构造函数返回并完成类初始化之前线程也不会运行,因此会出现死锁
结论:不能并行执行类初始化代码。类初始化必须在单个线程中完成
如果需要,您可以在类初始化期间启动线程,但您不能等待它们完成(如果它们也访问您的类,那么它们没有访问的意义是什么?)。我尝试了您的代码并做了两件事:
并行的静态内部类。。。以防万一;这没有改变任何事情
Parallel.isFlag()
上,所以我尝试用true
替换调用。。。成功了李>
所以,我做了一点研究,发现了这一点,这听起来像是对正在发生的事情的一个有希望的解释:
具体而言,本部分:
对于每个类或接口C,都有一个唯一的初始化锁LC。从C到LC的映射由Java虚拟机实现自行决定。初始化C的步骤如下:
private static Parallel i=new Parallel()时启动类初始化代码>并启动线程。然后它在锁存器上等待。等待()
。并行
的类对象应指示初始化“正在进行”。
Inside constructor.
Starting thread.
Starting thread.
Starting thread.
Starting thread.
Sleeping 2 seconds.
Leaving constructor.
Running thread.
Running thread.
Running thread.
Running thread.
public static void run() {
int count = 4;
CountDownLatch latch = new CountDownLatch(4);
for (int i = 0; i < count; i++) {
Thread t = new Thread(() -> {
try {
Thread.sleep(2000);
latch.countDown();
} catch (InterruptedException ex) {
Logger.getLogger(Parallel.class.getName()).log(Level.SEVERE, null, ex);
}
});
System.out.println("Starting thread.");
t.start();
}
try {
System.out.println("Current count: " + latch.getCount());
latch.await();
System.out.println("Current count: " + latch.getCount());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}