Concurrency does not have to depend on multithreading (such as common multi-process concurrency in PHP), but talking about concurrency in Java is mostly related to threading.
Thread is a more lightweight scheduling execution unit than the process, the introduction of a thread, can be a process of resource allocation and execution of scheduling , each thread can share process resources (memory address, file I/O, etc.), and can be independently dispatched ( Threads are the basic unit of CPU scheduling ).
The mainstream operating system provides thread implementations, while the Java language provides uniform handling of threading on different hardware and operating system platforms, and each instance of the Java.lang.Thread class that has executed start () and has not yet ended represents a thread. We note that the thread class differs significantly from most Java APIs, and all of its key methods are declared as native. In the Java API, a native approach often means that this method is not used or is not platform-agnostic (and, of course, possible to use the native method for efficiency, but usually the most efficient means are platform-related).
Implementation of Threads
There are 3 main ways to implement threads: using kernel threading implementations, using user thread implementations , and using user-line loads lightweight process blending implementations.
Using kernel thread implementations
The kernel thread (kernel-level thread,klt) is a thread that is supported directly by the operating system kernel (Kernel, the kernel) , which is thread-switched by the kernel, and the kernel dispatches the thread through the Manipulation Scheduler (Scheduler). and is responsible for mapping the thread's tasks to each processor. Each kernel thread can be considered a clone of the kernel, so that the operating system has the ability to handle multiple things simultaneously, and a multithreaded kernel is called a multithreaded kernel (multi-threads Kernel).
programs typically do not go directly to kernel threads, but instead use a high-level interface of kernel threads-lightweight processes (light Weight PROCESS,LWP), which are the threads that we normally talk about , Because each lightweight process is supported by a kernel thread, it is only possible to have a lightweight process by supporting kernel threads first. The 1:1 relationship between this lightweight process and kernel threads is called a one-to-one threading model.
Because of kernel threading support, each lightweight process becomes a separate dispatch unit, even though a lightweight process is blocked in the system call and does not affect the entire process to continue working, but the lightweight process has its limitations:
First, because it is based on kernel threads, various threading operations, such as creation, destruction, and synchronization, require system calls. System calls are relatively expensive and need to be switched back and forth between the user mode and the kernel state (Kernel mode).
Second, each lightweight process needs to have a kernel thread to support it, so a lightweight process consumes a certain amount of kernel resources (such as the stack space of kernel threads), so the number of lightweight processes that a system supports is limited.
Using the user thread implementation
Broadly speaking, a thread can be considered a user thread as long as it is not a kernel thread, so, from this definition, the lightweight process is also a user thread, but the implementation of the lightweight process is always built on the kernel, and many operations are thread,ut. Efficiency will be limited.
In the narrow sense, the user thread refers to the line libraries which is completely built on the user space, and the system kernel cannot perceive the implementation of thread existence. The creation, synchronization, destruction, and scheduling of user threads are completely done in the user state without the need for kernel help. If the program is implemented properly, this thread does not need to switch to the kernel state, so the operation can be very fast and low-consumption, can also support a larger number of threads, some high-performance database multithreading is implemented by the user thread. The 1:n relationship between this process and the user thread is called a one-to-many threading model.
the advantage of using a user thread is that no system kernel support is required and the disadvantage is that there is no system kernel support, and all thread operations require the user program to handle it themselves. Thread creation, switching, and scheduling are all issues to consider, and since the operating system allocates processor resources only to processes, problems such as "How to handle blocking," "How to map threads to other processors in a multiprocessor system" can be extremely difficult or impossible to complete. Thus, the use of user-thread implementation of the program is generally more complex, the "complex" and "program to do their own threading", does not restrict the program must write a complex implementation of the user thread code, using the user thread of the program, many rely on a specific line libraries to complete the basic thread operation, These complexities are encapsulated in the online libraries, in addition to the previous multi-threaded programs that do not support multithreaded operating systems (such as DOS) and a few special requirements, there are fewer programs using user threads, and Java, Ruby and other languages have used the user thread, and eventually abandon the use of it.
Using the user line loads lightweight process hybrid implementation
In addition to relying on kernel threading implementations and being implemented entirely by the user program itself, threads also have an implementation that uses kernel threads with user threads. In this hybrid implementation, there are both user threads and lightweight processes. User threads are still fully built into user space, so the creation, switching, and destruction of user threads are still inexpensive and can support large-scale user-thread concurrency. The lightweight process supported by the operating system serves as a bridge between the user thread and the kernel thread, which can use the thread scheduling and processor mappings provided by the kernel, and the user thread's system calls are done through a lightweight thread . Greatly reduces the risk of the entire process being completely blocked. In this blending mode, the number of user threads versus lightweight processes is variable, which is the n:m relationship. Many Unix-series operating systems, such as Solaris, HP-UX, and so on, provide N:M threading model implementations.
Implementation of Java Threads
for Sun JDK, both the Windows and Linux editions are implemented using a one-on-one threading model, and a Java thread is mapped to a lightweight process, because the threading model provided by Windows and Linux systems is single- .
In the Solaris platform, because the threading characteristics of the operating system can support one-to-one (through bound threads or alternate libthread implementations) and many-to-many (via Lwp/thread Based Synchronization implementation), so that two platform-specific virtual machine parameters are also available in the Solaris version of the JDK:-xx:+uselwpsynchronization (default) and-xx:+ Useboundthreads to explicitly specify which threading model the virtual machine uses.
Java Thread Scheduling
Thread scheduling refers to the process of assigning processor rights to threads by the system, and there are two main scheduling methods, namely cooperative thread scheduling (cooperative threads-scheduling) and preemptive thread scheduling . (Preemptive threads-scheduling).
Co-scheduling
If you use a multi-threaded system with coordinated scheduling, the execution time of the thread is controlled by the thread itself, and after the thread has executed its own work, the active notification system switches to another thread. The biggest benefit of collaborative multithreading is that it is simple to implement, and because threads are going to do their own thing after the thread switch, the switch operation is known to the thread itself, so there is no thread synchronization problem. The "co-routines" in the Lua language are such implementations. The downside is also obvious: the thread execution time is not controllable, and even if a thread is writing a problem, and the system is never told to switch threads, the program will always be stuck there. A long time ago, the Windows 3.x system was used to achieve multi-process multi-tasking, rather unstable, a process that insists on not giving up CPU execution time can cause the whole system to crash.
Preemptive scheduling
if a multi-threaded system with preemptive scheduling is used, then each thread will be assigned execution time by the system, and the thread's switchover is not determined by the thread itself (in Java, Thread.yield () can yield execution time, but the thread itself has no way of getting execution time). In this way of implementing thread scheduling, the execution time of threads is system controllable, and there is no problem that one thread will cause the whole process to block, and the thread scheduling method used by Java is preemptive scheduling. in subsequent versions of the JDK, it is possible to provide a coroutines way to multitask . In contrast to the previous example of Windows 3.x, the Windows 9x/nt kernel uses preemption to implement multiple processes, and when a process is out of order, we can also use Task Manager to "kill" the process without causing the system to crash.
Thread Priority
Although Java thread scheduling is automatic, we can "suggest" that the system allocates a little more execution time to some threads, while others can allocate less than one o'clock--this can be done by setting the thread priority. The Java language sets a total of 10 levels of thread priority (Thread.min_priority to thread.max_priority), and when two threads are in the ready state, the higher the priority thread, the more easily the system chooses to execute. However, thread prioritization is not too reliable because Java threads are implemented by mapping to the native thread of the system, so thread scheduling ultimately depends on the operating system, although many operating systems now provide thread prioritization, but not necessarily with Java thread priority one by one , such as Solaris with 2147483648 (2) priority, but there are only 7 windows, more than the Java thread priority system Fortunately, left a bit of space in the middle, but less than the Java thread priority system, You have to have a few priority cases, table 12-1 shows the relationship between the Java thread priority and the Windows thread priority, and the Windows platform JDK uses the remaining 6 thread priorities in addition to thread_priority_idle.
As mentioned above, "thread priority is not too good", not only that the different priorities on some platforms will actually become the same, but there are other situations where we cannot rely too much on priority: the priority may be changed by the system itself. For example, in a Windows system there is a function called "Priority thrusters" (which can be turned off), and its approximate effect is that when the system discovers that a thread is performing in particular "hard work", it may pass the thread priority to allocate execution time to it. Boosting Therefore, we cannot use precedence in the program to completely and accurately determine which of the threads that a set of states are ready to execute first.
Thread state transitions
The Java language defines 5 thread states, and at any point in time, a thread can have only one of these states, and the 5 states are as follows.
- NEW: A thread that has not been started since it was created is in this state.
- Run (runable): Runable includes running and ready in the operating system thread state, that is, a thread in this state may be executing, or it may be waiting for the CPU to allocate execution time to it.
- Wait indefinitely (Waiting): Threads in this state are not allocated CPU execution time, and they wait for Cheng to wake up from another line. The following method causes the thread to fall into an infinite waiting state:
-
- The Object.wait () method of the timeout parameter is not set.
- The Thread.Join () method of the timeout parameter is not set.
- Locksupport.park () method.
4. Deadline wait (Timed waiting): Threads in this state will not be allocated CPU execution time, but they will be automatically awakened by the system after a certain amount of time without waiting for the other lines to wake up Cheng. The following method causes the thread to enter a time-limited wait state:
-
- Thread.Sleep () method.
- The Object.wait () method of the timeout parameter is set.
- The Thread.Join () method of the timeout parameter is set.
- Locksupport.parknanos () method.
- Locksupport.parkuntil () method.
5. Blocking (Blocked): The thread is blocked, the difference between "blocking state" and "waiting state" is that "blocking state" waits to acquire an exclusive lock, which occurs when another thread discards the lock, while the "waiting state" is waiting for a period of time, or a wake-up action occurs. The thread will enter this state when the program waits to enter the synchronization area.
6. End (Terminated): The thread state of the thread has been terminated and the thread has finished executing.
Threading model for Java