Java daemon threads from a JVM Perspective

Keywords: Java jvm network

Java Multithreading Series 7.

Let's talk about another feature of threads: daemon threads or user threads?

Let's first look at the annotations for the Thread.setDaemon() method, as shown below.

  1. Marks this thread as either a daemon thread or a user thread.

  1. The Java Virtual Machine exits when the only threads running are all daemon threads.
  2. This method must be invoked before the thread is started.

Three points of information are mentioned here, one by one, to explain:

Official characteristics

1. User thread or daemon thread?

Java threads are divided into two categories, one is user threads, that is, when we create threads, the default one is daemon = false; the other is daemon thread, which is when we set daemon = true.

The general relationship between them is that user thread is the thread running in the foreground and daemon thread is the thread running in the background. In general, daemon thread is to provide some services for user thread. For example, in Java, we often say that GC memory recovery threads are daemon threads.

2. JVM coexists with user threads

When all user threads are executed and only daemon threads are running, the JVM exits. Read the online materials and some books, all have this sentence, but also have this sentence, did not explain why, as if this sentence became a theorem, do not need to prove the appearance. Now that we have recently built a JVM Debug environment, we need to find out. It took a long time to find out.

We see the JVM source thread.cpp file, which is the code to implement threads. We use the above sentence to show that there is a place to monitor the current number of non-daemon threads, otherwise how can we know that only daemon threads are left? It's very likely that in the method of removing threads, follow this line of thought, let's look at the file's remove() method. The code is as follows.

/**
 * Remove thread p
 */
void Threads::remove(JavaThread* p, bool is_daemon) {

  // Reclaim the ObjectMonitors from the omInUseList and omFreeList of the moribund thread.
  ObjectSynchronizer::omFlush(p);

  /**
   * Create a monitoring lock object ml
   */
  // Extra scope needed for Thread_lock, so we can check
  // that we do not remove thread without safepoint code notice
  { MonitorLocker ml(Threads_lock);

    assert(ThreadsSMRSupport::get_java_thread_list()->includes(p), "p must be present");

    // Maintain fast thread list
    ThreadsSMRSupport::remove_thread(p);

    // Current threads minus 1
    _number_of_threads--;
    if (!is_daemon) {
        /**
         * Reduce the number of non-daemon threads by 1
         */
      _number_of_non_daemon_threads--;

      /**
       * Wake up the thread waiting in the destroy_vm() method when the number of non-daemon threads is 1
       */
      // Only one thread left, do a notify on the Threads_lock so a thread waiting
      // on destroy_vm will wake up.
      if (number_of_non_daemon_threads() == 1) {
        ml.notify_all();
      }
    }
    /**
     * Remove threads
     */
    ThreadService::remove_thread(p, is_daemon);

    // Make sure that safepoint code disregard this thread. This is needed since
    // the thread might mess around with locks after this point. This can cause it
    // to do callbacks into the safepoint code. However, the safepoint code is not aware
    // of this thread since it is removed from the queue.
    p->set_terminated_value();
  } // unlock Threads_lock

  // Since Events::log uses a lock, we grab it outside the Threads_lock
  Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p));
}

I added some comments to it and found that, as we thought, there was a record of the number of non-daemon threads, and when the non-daemon threads were 1, the thread waiting in the destory_vm() method would wake up. We confirmed that we had found the thread code that triggered the wake-up monitoring JVM to exit when the number of non-daemon threads was 1. Next, let's look at the destory_vm() code, which is also under the thread.cpp file.

bool Threads::destroy_vm() {
  JavaThread* thread = JavaThread::current();

#ifdef ASSERT
  _vm_complete = false;
#endif
  /**
   * Waiting for yourself to be the last non-daemon thread condition
   */
  // Wait until we are the last non-daemon thread to execute
  { MonitorLocker nu(Threads_lock);
    while (Threads::number_of_non_daemon_threads() > 1)
        /**
         * If the number of non-daemon threads is greater than 1, wait
         */
      // This wait should make safepoint checks, wait without a timeout,
      // and wait as a suspend-equivalent condition.
      nu.wait(0, Mutex::_as_suspend_equivalent_flag);
  }

  /**
   * The following code is the logic to turn off the VM
   */
  EventShutdown e;
  if (e.should_commit()) {
    e.set_reason("No remaining non-daemon Java threads");
    e.commit();
  }
  ...... Eliminate the remaining code
}

We see here that when the number of non-daemon threads is greater than 1, it waits until the remaining non-daemon threads exit the JVM after the thread has executed. At this point, there is another point to locate. When do you call the destroy_vm() method? Or by looking at the code and the comments, it was found that it was triggered after the main() method was executed.

In the JavaMain() method of the java.c file, the LEAVE() method is finally called after execution, which calls (* vm) - > Destroy JavaVM (vm); to trigger JVM exit, and finally calls the destroy_vm() method.

#define LEAVE() \
    do { \
        if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { \
            JLI_ReportErrorMessage(JVM_ERROR2); \
            ret = 1; \
        } \
        if (JNI_TRUE) { \
            (*vm)->DestroyJavaVM(vm); \
            return ret; \
        } \
    } while (JNI_FALSE)

So we also know why main threads can exit first than sub-threads. Although the main thread invokes the destroy_vm() method before exiting, it waits for the non daemon thread to execute in the destroy_vm() method. If the child thread is a non daemon thread, JVM will wait and will not quit immediately.

Let's summarize this point: Java programs trigger JVM exit when the main thread executes exit, but the JVM exit method destroy_vm() waits for all non-daemon threads to execute. It counts the number of non-daemon threads with the variable number_of_non_daemon_threads, which adds or subtracts threads when adding or deleting threads.

Another derivation is that when the JVM exits, all remaining daemon threads will be discarded, neither the final part of the code nor the stack unwound operation (that is, no catch exception). Obviously, the JVM exits, and the daemon thread naturally exits, which is a feature of the daemon thread, of course.

3. Is it a man or a woman? Born to be

This is a better understanding, that is, whether a thread is a user thread or a daemon thread, it has to be determined before the thread starts. Before calling the start() method, it is only an object, not a thread mapped to the JVM. At this time, the daemon property can be modified. After calling the start() method, a thread in the JVM maps the thread object, so it can not be modified.

Other characteristics

1. The daemon thread property inherits from the parent thread

I don't need to write code to verify this. Look directly at the Thread source code construction method, you can see that the code is as follows.

private Thread(ThreadGroup g, Runnable target, String name,
               long stackSize, AccessControlContext acc,
               boolean inheritThreadLocals) {
   ...Omit a bunch of code
    this.daemon = parent.isDaemon();
   ...Omit a bunch of code
}

2. Daemon threads have lower priority than user threads

Seeing that many books and materials say so, I doubt it. So write the following code to test if the daemon thread priority is lower than the user thread?

public class TestDaemon {
    static AtomicLong daemonTimes = new AtomicLong(0);
    static AtomicLong userTimes = new AtomicLong(0);

    public static void main(String[] args) {
        int count = 2000;
        List<MyThread> threads = new ArrayList<>(count);
        for (int i = 0; i < count; i ++) {
            MyThread userThread = new MyThread();
            userThread.setDaemon(false);
            threads.add(userThread);

            MyThread daemonThread = new MyThread();
            daemonThread.setDaemon(true);
            threads.add(daemonThread);
        }

        for (int i = 0; i < count; i++) {
            threads.get(i).start();
        }

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("daemon Statistics:" + daemonTimes.get());
        System.out.println("user Statistics:" + userTimes.get());
        System.out.println("daemon and user Difference time:" + (daemonTimes.get() - userTimes.get()) + "ms");

    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            if (this.isDaemon()) {
                daemonTimes.getAndAdd(System.currentTimeMillis());
            } else {
                userTimes.getAndAdd(System.currentTimeMillis());
            }
        }
    }
}

The results are as follows.

Result 1:
daemon statistics: 1570785465411405
 user statistics: 1570785465411570
 The difference time between daemon and user: - 165 ms

Result 2:
daemon statistics: 1570786615081403
 user statistics: 1570786615081398
 The difference time between daemon and user is 5 ms

Amazingly, there is little difference, but in this case I can't define that daemon threads and user threads have the same priority. Looking at the JVM code, I can't find that the priority of the daemon thread is lower than that of the user thread. This point is still doubtful. Friends with knowledge can leave a message to say something and learn from each other.

summary

To sum up the points explained in this article, one is that threads are divided into two types: user threads and daemon threads; if you want to set threads as daemon threads, you need to set the daemon property before the thread calls the start() method; and from the perspective of JVM source code, you can analyze why the JVM automatically exits when the user threads are all executed. Then it explains that the daemon thread has inheritance and the parent thread is the daemon thread, so the default of the child thread is the daemon thread. In addition, it raises its own questions about the fact that the priority of the daemon thread mentioned in some books and materials is lower than that of the user thread, and hopes that the friends who have the understanding can help to answer them.

If you think that this article has gained a lot from reading, please take a look at it and support it. It's not easy to be original.

Recommended reading

Write Java code for many years, and finally debug to the JVM

Original | The latest and simplest openjdk13 code compilation for the whole network

Understanding Java thread priority, but also know the priority of the corresponding operating system, or you will trample on the pit

The most basic knowledge of threads

The boss told you to stop blocking.

Eating a fast food can learn serial, parallel, and concurrent

Make a cup of tea and learn from each other.

How much does the process know?

Design patterns look and forget, forget and see?

Backstage reply to "Design Patterns" can obtain "One Story One Design Patterns" e-book

I think this article is helpful for forwarding & a little praise, thank you for your friends!

This article by the blog article multiple platform OpenWrite Release!

Posted by SpinCode on Mon, 14 Oct 2019 18:13:31 -0700