简体   繁体   中英

How does a Java application keeps on running even when main thread has finished execution?

When an application is started via main method in Java and it spins other user threads(not daemon) for tasks such as:

  • User Thread-01: Loading Cache From Database
  • User Thread-02: Perform application startup tasks like running diagnostics
  • User Thread-03: Miscellaneous tasks

The user threads started by main thread will complete execution at some point in time and terminate. This will cause the main thread to terminate eventually and the application will stop running. But, as we see so many applications which once started keep on running and if we take a thread dump, we can see the main thread at the very bottom. This means the main thread is not terminated which means the user threads started are not terminated.

How is this achieved? I mean what are standard programming constructs and logics to keep threads alive without running them through an infinite for or while loop?

Thanks for going through the question. Every helpful reply will add to our knowledge.

Executors framework

You said:

spins other user threads

Hopefully you are not addressing Thread objects directly. Since Java 5, for most purposes we can be using the Executors framework to manage work on background threads. See tutorial by Oracle.

ExecutorService es = Executors. … ;
es.submit( yourRunnableOrCallableHere ) ;  // Optional: Capture the returned `Future` object to track success/failure of your task.
…
es.shutdown() ;

Background threads ending has no effect on main thread

You said:

The user threads started by main thread will complete execution at some point in time and terminate. This will cause the main thread to terminate eventually and the application will stop running.

Not correct.

The main thread ends when it has no more work to do. Background threads may end before the main thread, or after the main thread. The background threads termination does not cause the main thread to end.

Here is some example code to demonstrate this behavior. This app performs a thread-dump and then runs a task in the background which also performs a thread dump. Both main thread and background thread sleep a few seconds.

package work.basil.example;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Threading
{
    public static void main ( String[] args )
    {
        Threading app = new Threading();
        app.demo();
    }

    private void demo ( )
    {
        System.out.println( "---------------|  main thread  |------------------------------------" );
        System.out.println( "Bonjour. " + Instant.now() );
        System.out.println( Threading.threadDump( true , true ) );

        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(
                ( ) -> {
                    System.out.println( "---------------|  background thread  |------------------------------------" );
                    System.out.println( "DEBUG - Starting background thread. " + Instant.now() );
                    System.out.println( "DEBUG - Sleeping background thread. " + Instant.now() );
                    try {Thread.sleep( Duration.ofSeconds( 2 ).toMillis() );} catch ( InterruptedException e ) {e.printStackTrace();}
                    System.out.println( Threading.threadDump( true , true ) );
                    System.out.println( "DEBUG - Ending background thread. " + Instant.now() );
                }
        );

        executorService.shutdown();  // Always be sure to shutdown  your executor services.
        try {Thread.sleep( Duration.ofSeconds( 5 ).toMillis() );} catch ( InterruptedException e ) {e.printStackTrace();}
        System.out.println( "---------------|  main thread  |------------------------------------" );
        System.out.println( Threading.threadDump( true , true ) );
        System.out.println( "DEBUG - Main thread ending. " + Instant.now() );
    }

    // `threadDump` method taken from: https://www.baeldung.com/java-thread-dump
    private static String threadDump ( boolean lockedMonitors , boolean lockedSynchronizers )
    {
        StringBuffer threadDump = new StringBuffer( System.lineSeparator() );
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        for ( ThreadInfo threadInfo : threadMXBean.dumpAllThreads( lockedMonitors , lockedSynchronizers ) )
        {
            String message = "Thread: " + threadInfo.getThreadId() + " | " + threadInfo.getThreadName();
            threadDump.append( message ).append( System.lineSeparator() );
//            threadDump.append( threadInfo.toString() );
        }
        return threadDump.toString();
    }
}

When we sleep the background thread for less time than the main thread (2 seconds versus 5), notice that the main thread continues. The background thread ending has no effect on the main thread continuing or ending.

When run, notice how launching the executor service with a submitted task results in two more threads with IDs 14 & 15 here. Then after the background task ends and the executor service is closed, the thread with ID 14 disappears. Notice how the main thread does not end , continuing to do work — contradicting your statement in the Question.

/Library/Java/JavaVirtualMachines/jdk-16.jdk/Contents/Home/bin/java -javaagent:/Users/basilbourque/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5981.155/IntelliJ IDEA 2020.3 EAP.app/Contents/lib/idea_rt.jar=49389:/Users/basilbourque/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5981.155/IntelliJ IDEA 2020.3 EAP.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/basilbourque/IdeaProjects/Loom/target/classes work.basil.example.Threading
---------------|  main thread  |------------------------------------
Bonjour. 2020-12-18T07:30:21.107455Z

Thread: 1 | main
Thread: 2 | Reference Handler
Thread: 3 | Finalizer
Thread: 4 | Signal Dispatcher
Thread: 11 | Common-Cleaner
Thread: 12 | Monitor Ctrl-Break
Thread: 13 | Notification Thread

---------------|  background thread  |------------------------------------
DEBUG - Starting background thread. 2020-12-18T07:30:21.268025Z
DEBUG - Sleeping background thread. 2020-12-18T07:30:21.268225Z

Thread: 1 | main
Thread: 2 | Reference Handler
Thread: 3 | Finalizer
Thread: 4 | Signal Dispatcher
Thread: 11 | Common-Cleaner
Thread: 12 | Monitor Ctrl-Break
Thread: 13 | Notification Thread
Thread: 14 | pool-1-thread-1
Thread: 15 | Attach Listener

DEBUG - Ending background thread. 2020-12-18T07:30:23.275729Z
---------------|  main thread  |------------------------------------

Thread: 1 | main
Thread: 2 | Reference Handler
Thread: 3 | Finalizer
Thread: 4 | Signal Dispatcher
Thread: 11 | Common-Cleaner
Thread: 12 | Monitor Ctrl-Break
Thread: 13 | Notification Thread
Thread: 15 | Attach Listener

DEBUG - Main thread ending. 2020-12-18T07:30:26.271499Z

Process finished with exit code 0

For fun, try that code but reverse the durations. Use 5 seconds versus 2 seconds to see the background threads outlive the main thread.

A web server is busy listening, all the time

You said in a comment :

visualize it like this... we have a website which is alive even when no one is opening the web page in the browser... which means the application is running even when no one is interacting with it... if we say that it is the web server which is running in background and not the actual web application.... but, then how is the web application is running indefinitely even when no one is interacting with it.

Regarding "we have a website which is alive even when no one is opening the web page in the browser", no your web site is not "alive". Without any pending HTTP request to handle, your Java Servlet is not executing. Your servlet is loaded and initialized, but is not executing until a request arrives.

Regarding "which means the application is running even when no one is interacting with it", no your web app is not running, as I said above. Your Java servlet code is not executing. When a request arrives, the web container automatically invokes your servlet's code in a thread. Ultimately that servlet code results in the generation of content sent back to the web browser in a response. Your servlet code's execution ends. The thread used for that execution either ends or is returned to a thread pool (an internal decision for your web container to make). I am ignoring Push technology and WebSockets for the sake of simplicity.

Regarding: "if we say that it is the web server which is running in background and not the actual web application", the web server is always running an extra thread to listen for incoming requests.

➥ This may be the root of your confusion: A web server is always busy , busy listening for incoming connections, whether or not executing your Java servlet code.

  • A web server has one thread dedicated to working with the host OS to listen on the network for incoming connections.
  • A web server launches other threads as needed to respond to a request by formulating and sending a response.

Regarding: "how is the web application is running indefinitely even when no one is interacting with it", you are forgetting that the host OS is interacting with the web container, whether or not users are interacting with your web app. The web container maintains a thread listening for incoming connections. That thread sits blocked, waiting on a notification by the host operating system's networking stack of an incoming request. (My description here is generalized and simplified — I'm no networking expert — but suffices for the point being made here.)

When a request comes in over the network, the host OS informs the web container. This notification unblocks the listening thread. The listening thread dispatches the request to a new thread, resulting in your Java servlet's code executing. Meanwhile the request-listening thread of the web container goes back to being blocked to await another notification from the host OS' network stack about yet another incoming request.

That blocked listening thread is what explains/enables the web server running continuously and indefinitely. Your web app, in contrast, runs in spurts, only in response to a request.

Your question is a credit to the genius and success of Java Servlet technology. The very purpose of Java Servlet is to abstract away all these details about listening for network activity, translating packets to text to decipher a request, parsing that request, mapping the request's contents to being the responsibility of a particular servlet, ensuring that particular servlet is loaded and initialized, and eventually calling a specific method in your servlet code defined by the Servlet spec.

A user app is busy waiting for input, all the time

Similar to a web server being always busy listening for incoming requests, console apps and gui apps are both always busy listening for user input. They may seem idle at times, but are not.

While user apps do not spin continuously on the CPU, they do maintain a thread that works with the host OS to be informed of user events such as keyboard typing and mouse motion/clicks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM