5

The code is below, in the Main method I call a static method:

public class Main {
    public static void main(String[] args) {
        MyFactory.getSomething();
        System.out.println("over"); 
    }
}

In the static method, let the main thread wait, then by demo-thread wake up. But the main thread does not wake up and I don't know why.

import java.util.concurrent.TimeUnit;

public class MyFactory {
    private static Object lock = new Object();

    static{
        init();
    }

    private static void init(){
        new Thread(()->{
            try {
                // make this thread run after main thread
                TimeUnit.SECONDS.sleep(3);
                System.out.println("task run...");
                synchronized (lock){
                    lock.notifyAll();  // notify main thread
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"demo-thread").start();

        synchronized (lock){
            try {
                System.out.println("waiting...");
                lock.wait();  // wait
                System.out.println("wake up");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    public static Object getSomething(){
        return null;
    }
}

The output is below:

waiting...

My expected should be like this:

waiting...
task run...
wake up
over

Here is the demo code on Github You can download it to run.


My try:

I see losts of people say may be a dead lock cause the problem.I don't think so.Because we don't see task run... info in the output. So the demo thread not aquire the lock at all.

When I change the code In MyFactory.java.(Add a new thread to run init method)

from this

static{
  init();
}

to this.

static{
  new Thread(()->init()).start();
}

Every thing work fine.The program not block.

Here is the out put after I change the code .

over
waiting...
task run...
wake up

Back to the original question "Why main thread not wake up"? I think the JVM didn't load the MyFactory.class completely due to main thread blocking。I don't know if my guess is right???

Q10Viking
  • 982
  • 8
  • 9
  • 1
    What do you see in the output? Can you add its output as well? – Kozmotronik Apr 29 '23 at 12:26
  • 1
    Because you locked the main thread. – Roman C Apr 29 '23 at 12:27
  • @Roman C.yes. I locked the main thread.But I let the new thread to wake up it.But the main thread is not wake up .I find no dead lock in the code. – Q10Viking Apr 29 '23 at 12:50
  • @Q10Viking It's not true, the new thread is not able to unlock it. – Roman C Apr 29 '23 at 12:59
  • 1
    @RomanC Yes but why? That's what is being asked. – m0skit0 Apr 29 '23 at 13:05
  • 4
    For those who are voting-to-close or attempting to answer: You're missing it. The CRUCIAL question here is: __Why in the blazes is that thread not starting??__ - try it, paste this code, compile it, run it. Add a print to the run() block of that thread right up top. It NEVER STARTS. A proper answer to this question requires intricate knowledge of the JVM class init rules and the vagaries of what thread constructor locks on, to answer why in this peculiar case the thread doesn't actually start. 2 answers so far that missed it. – rzwitserloot Apr 29 '23 at 13:08
  • 3
    For the vote-to-closers closing for 'insufficient detail': Replace the log.info with sysout and this is a self-contained example, surely that is sufficient detail! – rzwitserloot Apr 29 '23 at 13:08
  • @m0skit0 Because it's very poor written. The same question should be asked million times and it cannot be answered in a few words. – Roman C Apr 29 '23 at 13:09
  • 2
    The key point here is that starting threads in the middle of class initialization (because you're in the context of that `static {}` block) does not work - it delays the starting of it until initialization is done, which it'll never be, because during initialization you invoke `wait()` on a lock. I've tried to find out why and I can't find it (hence, this is a comment, not an answer). – rzwitserloot Apr 29 '23 at 13:12
  • 2
    Its not starting because the lambda is equivalent to a static method, and that can't be called unless the class has completed initialisation. The thread code is awaiting init to end. Its similar to / duplicates the issues seen [here](https://stackoverflow.com/questions/75712988/why-does-joining-a-no-op-virtual-thread-in-a-static-initialiser-block-indefinite) and [here](https://stackoverflow.com/questions/34820066/why-does-parallel-stream-with-lambda-in-static-initializer-cause-a-deadlock). – DuncG Apr 29 '23 at 13:50
  • 1
    The lock is not to do with `synchronized`, you can reproduce with starting a Thread with any `Runnable` that references a field of the class. For example replace init() by `new Thread(() -> System.out.println("Hello World "/* +lock */),"demo-thread").start()`. The code will never run if you uncomment use of `lock`. – DuncG Apr 29 '23 at 14:18
  • 1
    @xerx593 if code `static{ init();}` change to `static{ new Thread(()->init()) }` will work。So that's not a dead lock problem. – Q10Viking Apr 29 '23 at 14:20
  • 1
    the one (main thread approach) was a classical "dead lock": the main thread "owns" the lock and waits it (blockingly) forever., by introducing another thread (*asynchronizing* the `lock.wait()` indeed) you give jvm a chance for "Reentrant Synchronization" (https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html) – xerx593 Apr 29 '23 at 15:48
  • @xerx593 Cool!!! Can you please reopen it to write this answer?Thank you very much – Q10Viking Apr 29 '23 at 15:51
  • 1
    Thank you for the flowers :) here some [jls-link](https://docs.oracle.com/javase/specs/jls/se20/html/jls-14.html#jls-14.19) , but i fear this, the tiny chapter ( + *rest* ) of [tutorial](https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html) and maybe [ReentrantLock javadoc](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html) are the best documentation about... – xerx593 Apr 29 '23 at 16:20
  • @rzwitserloot Thank you for the advice。I have change `log.info` to `sysout` in the code.My original intention was to use `log.info` to see the thread name – Q10Viking Apr 29 '23 at 18:22

1 Answers1

4

I'm not an expert in JVM internals but I think the problem lies in here:

static {
    init();
}

I suspect this will be executed when the JVM classloader is loading the class. However this class loading will never finish since you're blocking the classloader itself, and thus execution of the rest of the program will never resume, and thus the other thread is never started.

If you remove the static block and call it directly:

public static void main(String[] args) {
    // ...
    MyFactory.init();
    MyFactory.getSomething();
    // ...
}

You will see that it works properly.

More details under JLS 12.4.2

On a side note, sleeping is not a good idea to synchronize threads.

m0skit0
  • 25,268
  • 11
  • 79
  • 127
  • @SuchandraT I don't see any mention of class loading or JVM in your first line. What I understand from "Your notification is already completed due to the fact that it was written in static block", is the notify has already been called before the wait, which is not the case. – m0skit0 Apr 29 '23 at 13:17