This is one of the questions Angela was asked in her previous interview, following the previous article on switching between user and kernel mode for thread calls, and later on on Java concurrency.

Interviewer: I heard from the previous interviewer that you have a good grasp of Java concurrency, let’s have a deep communication;

Me: Looking at the thin connective tissue in the interviewer’s head, I already thought this was going to be a tough interview, but having read Angela’s blog beforehand, I replied: Ahem, it’s ok.

Interviewer: Have you used Java threads?

Me: Yes.

Interviewer: Can you tell me something about Java threads and the operating system?

Me: Ah!!

It’s not supposed to be like this. It’s not supposed to start with synchronized or volatile, then thread pools and AQS.

Me: Ok, I’ll do it in three paragraphs.

  • Threads in user mode
  • Kernel mode threads
  • Java thread source code

1. Threads in user mode

Stage 1:

In early days, the operating system is not the concept of threads, thread is added behind, the operating system initially only process, operating system, the allocation of resources of the lowest denomination is the process between process and process related isolation, each process has its own memory space, file descriptors, CPU scheduling process as minimum scheduling unit;

Stage 2:

In the early days of multithreading, threads were implemented in user space.

What do you mean? We all know, sharing the system space and user space memory system space is used to the operating system, the user space is used by the application, the application if you need to access the system space, the need for a system call, the switch from user mode to kernel mode, this detail can refer to my previous article: [about] the user space and kernel space

So how do you do multithreading in user space?

In fact, the operating system schedules the threads in the process dimension. The operating system does not care about your user thread switching. The application creates, maintains, and schedules the threads in user space. The model is shown below:

When threads are implemented in user space, the operating system is unaware of the existence of threads; the operating system can only see the process, but not the thread. All threads are implemented in user space. From the operating system’s point of view, each process has only one thread.

One of the benefits of this approach is that threading can be supported through library functions even if the operating system does not support it. In JDK1.1, the green thread is used instead of the original thread.

The following is an explanation of green thread, because green thread is not the focus of today, I will not go into detail.

Green Threads are threads that are scheduled by the running environment or virtual machine (VM), rather than by the local underlying operating system. Green threads do not rely on the underlying system functionality, simulating the operation of multiple threads whose management deployment takes place in user space rather than kernel space, so they can work in environments without native thread support. In Java 1.1, green threading (at least on Solaris) is the only threading model used in the JVM. Due to the limitations of using green threads compared to native threads, subsequent Java versions abandoned green threads in favor of native threads.

The advantages and disadvantages of this model are obvious:

Disadvantages: Because the operating system does not know the existence of threads, the CPU time slice switch is based on the process as a dimension, if a process in a thread to perform some time-consuming operations, will block the entire process. In addition, when a thread (green thread) in a process makes a system call, such as network IO, page break, etc., and the thread blocks, the operating system will block the entire process, even if the other threads in the process are still working.

Advantage: the use of library functions to achieve the thread switch, it eliminates the user mode to the kernel mode switch, this taste is not familiar, by the way, Go coroutine has borrowed part of this idea.

2. Kernel mode threads

After Java1.2. The JVM in Linux is implemented based on PThreads, and it can be directly said that Java threads are implemented in a 1:1 relationship depending on the operating system.

Threads in Java today are essentially threads in the operating system

In addition, I read a lot of information that the Implementation of Java threads is LWP(Lightweight process). In fact, since the Linux kernel 2.6, the implementation of Linux Threads has been changed to NPTL. NPTL addresses most of the POSIX incompatible features of LinuxThreads and provides better performance, scalability, and maintainability.

LinuxThread uses the 1 * 1 model, that is, each user-mode thread has a kernel management entity corresponding to it, which is the process, also known as LWP (lightweight process).

Those who want to know more about NPTL can go to see the detailed introduction of NPTL.

As we know, each thread has its own thread context, which includes a collection of the thread’s ID, stack, program counter, general purpose register, and so on. I always find the word context ambiguous, but I can’t find a better word to describe it.

Threads have their own independent context and are scheduled by the operating system, but there is a disadvantage that threads are too resource-intensive. For example, on Linux, the default stack size for a thread is 1 MB, so creating tens of thousands of threads on a single server is a bit of a struggle. So at the programming language level, coroutines came into being.

The coroutine mode is similar to a combination of the two methods, that is, switching threads in user mode and having the operating system do thread scheduling at the kernel level.

There is a mapping relationship between coroutines and operating system threads. For example, we built m coroutines, which need to be executed on N threads. This is the m: N scheme, which is also realized by operating system scheduling.

In addition, coroutines use stack memory on demand, so in theory you can easily create mega coroutines.

The best coroutine support is currently in go, but the OpenJDK community is currently adding coroutine support to the JDK.

3. Source code for threads

When we call new Thread(Runnable ***).start() in Java, how can we switch from user mode to kernel mode and send a system call to create a Thread in the OS kernel layer?

This can be used to drill down. The source code address will be posted at the end of the article, but the key point is to post the source code of the system call creation thread at the JVM layer.

Private native void start0();

Go to the thread.c file,

Line 44, method mapping; Follow JVM_StartThread into jVM.cpp

For Linux, see SRC /hotspot/ OS/Linux /os_linux.cpp

In this case, the Linux C library function is used to complete the system call, from the user mode to the kernel mode to complete the thread creation.

Source code address:

Thread.c

pthread_create

os_linux

Finally, lest I mislead you, hair is not connective tissue.

I’m going to have an operation tomorrow, head connective tissue group cutting operation, wish me luck!

Reference:

[Understanding java’s native threads and the jvm]