I had a very peculiar problem happening to me that I could not solved except splitting up the Problem into two classes.
I would like to know if there is maybe a solution without splitting the class and I would more importantly like to know if anybody has an idea why the Java Engine is deciding to act the way it does.
The Problem: I have a class with a static method, a static field and a constructor. The static field is initialized to an instance of the class itself. During the instance initialization I want to access the aformentioned static method. See the following code:
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();
}
}
This code runs absolutely fine. The output can be seen below:
Simple: true
Simple: true
Simple: true
Simple: true
The output is generated after I call the run()
method because the stativ field i is only initialized after I access the first static member of that class.
I now want to do the exact same thing except with multiple threads. See here:
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();
}
}
This returns nothing. The main thread is stuck at latch.await();
, while the other threads are stuck at . Edit: as shown by Jaims below, the threads don't even start at all.Parallel.isFlag()
This does not make any sense to me. Why is this not working, but the first case is? Essentially they are doing the same.
I would like to know how the Java Engine decides on when to wait and when not. Can this be changed somewhere in code?
Additionally, this has nothing to do with CountDownLatch but solely with the multithreading. Look at this final sample:
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();
}
}
This works fine. The output is as following:
NonParallel: true
NonParallel: true
NonParallel: true
NonParallel: true
Edit: none of this applies when the object initlization is not part of the class initilization. This is purely about class initialization which only happens when using a static object as described in this question. See here:
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();
}
}
This one works without any issue:
Parallel: true
Parallel: true
Parallel: true
Parallel: true
Answers:
Andreas provides an explanation as to what is going on.
Jaims is right in that the threads do not even start at all. This probably happens because they need the class to be initialized and they are immediately therefore blocked. (If we use runnables that are in their own classes instead of lambda or anonymous inner classes then they run normally, unless of course they acess the any static members of the class being initialized)
Yoshi provides a link and an excerpt from the the spec, and is therefore marked as the right answer, as this is what I wanted.