2

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 Parallel.isFlag(). Edit: as shown by Jaims below, the threads don't even start at all.

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.

nntvi
  • 55
  • 7
  • Just wondering: what is the problem that actually want to solve? As this design; especially the many static things in there ... have a certain smell of weird design. – GhostCat Aug 17 '16 at 17:09
  • What I am trying to is run a parallization backend using static accessors. It is totally trivial and probably better to switch to a non-static system, which I will probably do. I am more interested in the underlying technical issue. Is this just a rule in java that in this case we wait for a thread, even if we don't need to. This knowledge can become very interesting for other problems that do require static accessors. – nntvi Aug 17 '16 at 17:12
  • 2
    This doesn't make any sense: `Thread.currentThread().interrupt();` Why would a thread interrupt itself (except, maybe to re-set the interrupted flag after _handling_ an InterruptedException that was caused by some _other_ thread)? – Solomon Slow Aug 17 '16 at 17:49
  • The name of your `run()` method violates the Principle of Least Astonishment (Google it!). A better name would be `insureThatClassIsLoaded()` or `forceClassToLoad()`. – Solomon Slow Aug 17 '16 at 17:51
  • Starting threads from inside a constructor is a bad habit. It can sometimes lead to inconsistent/unexpected behavior. Google for "leaking this from constructor." – Solomon Slow Aug 17 '16 at 17:53
  • 2
    Actaully, doing any kind of real work inside a constructor is another POLA violation. Developers usually expect a constructor to do no more than put the new object into a useable, initial state. – Solomon Slow Aug 17 '16 at 17:55
  • Hi. Yes the interrupt thing was unecessary. Other than that, thanks for your comments and the helpful tips. You are absolutely right about constructor misuse an POLA violation. You are also right about leaking this from constructor. Look at this answer which describes exactly what is happening here by mentioning multi-threaded environments: http://stackoverflow.com/a/9851843/2065718 – nntvi Aug 18 '16 at 11:53

3 Answers3

2

When you call run(), the current thread will begin class initialization. Any code referring to the class, e.g. call to isFlag() will also require class initialization.

In your Simple and NonParallel versions, the current thread is doing it all, and recursive class initialization is allowed (ignored actually), so isFlag() is executed, even though the class initialization is not yet complete.

In your Parallel version however, the call to isFlag() is done from another thread, and so that other thread has to wait for the class to be fully initialized. Since your constructor won't return until the threads run, and the threads can't run until the constructor returns and completes the class initialization, you have a deadlock.

Conclusion: You cannot perform class initialization code in parallel. Class initialization has to complete in a single thread.

You can start threads during class initialization if you want, but you cannot wait for them to complete (if they also access your class, and what would be the point of they didn't?).

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Thanks for your answer. I know about the fact that I have a deadlock and that it is because the other threads need to wait. I just wanted to know why that is and where it is documented. Yoshi gives the correct answer in regards to the initialization lock and its documentation from the spec – nntvi Aug 18 '16 at 11:56
  • regarding your last sentence: in fact the threads don't even start a tall, look at Jaims answer. – nntvi Aug 18 '16 at 12:11
2

I tried your code and did two things:

  1. First, I made the lambda a static inner class of Parallel ... just in case; this didn't change anything.
  2. Since you commented that the threads are stuck on Parallel.isFlag() I tried replacing the call with just true... and it worked!

So, I did a little research and I found this, which sounds like a promising explanation for what is going on: http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2

Specifically this part:

For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:

  1. Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.

  2. If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.

(Emphasis added.) So this would suggest the following:

  1. Main thread started class initialization while evaluating private static Parallel i = new Parallel(); and started up the threads. Then it waited on latch.await(). Class object for Parallel should indicate that initialization is "in progress."
  2. Started threads also try to reference a static member of Parallel. Each thread sees that initialization is in progress and decides to wait until the Main thread (which is now waiting on the threads to count down the latch) is done. Clearly this is a deadlock.
yoshi
  • 171
  • 5
1

Your threads are not started until the object is created correctly. Consider the following snippet:

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() {

    }
}

It'll produce the following output:

Inside constructor.
Starting thread.
Starting thread.
Starting thread.
Starting thread.
Sleeping 2 seconds.
Leaving constructor.
Running thread.
Running thread.
Running thread.
Running thread.

The threads are started within the constructor 4 times, as the output shows. It starts sleeping for 2 seconds, leaves the constructor and then runs your threads. Not like it takes 2 seconds for your threads to run.

So the core issue with your problem, is that you're calling latch.await(), but your threads never get the chance to actually run. Meaning the latch isn't decremented and simply keeps waiting. You could move the logic to your run() method, but I'm not really sure what you're trying to achieve in the first place. 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();
    }
}
Jaims
  • 1,515
  • 2
  • 17
  • 30
  • Ok, but this doesn't happen when initializing the Class without it being a static field. This is what is throwing me off. – nntvi Aug 18 '16 at 12:09
  • When you initialize the class directly without static fields the output is as expected: Inside constructor. Starting thread. Starting thread. Starting thread. Running thread. Starting thread. Running thread. Running thread. Sleeping 2 seconds. Running thread. Leaving constructor. – nntvi Aug 18 '16 at 12:09
  • This is not about object initialization, but about class initialization. If you initialize the object without the class initialization aspect it works normally. I have updated my question. – nntvi Aug 18 '16 at 12:12
  • Another thing: if you do not create the runnable inline (lambda or anonymous class) then the threads do start and run as they do not need to wait for class initialization. – nntvi Aug 18 '16 at 12:23
  • @nanotvi A lot of valid and interesting points. Seems I over-simplified things. I suppose the answers given by Yoshi and Andreas are more well-thought of :) – Jaims Aug 18 '16 at 12:28
  • I liked your answer because it shows that the threads don't even start at all. I thought they stopped as isFlag() but obviously, they didn't. I have updated my question accordingly. – nntvi Aug 18 '16 at 12:31