Java multithreading (1) multithreading introductory articles

Java multithreading (1) multithreading introductory articles

1 Speaking of threads, first of all, let's talk about the process. The following is the definition of the process:

The process is the foundation of the operating system structure. It is the execution of a program. It is the activity that occurs when a program and its data structure are executed sequentially on the processor. It is the process of the program running on a data set. It is the system's resource allocation and An independent unit of scheduling.

Simply put, an exe file listed in a task manager can be understood as a process, such as QQ.exe is a process, and a process is the basic operating unit managed by the system.

1.1 What is a thread?

Thread is the smallest unit that the operating system can perform operation scheduling. It is included in the process and is the actual operation unit in the process.

In simple terms, threads can be understood as subtasks that run independently in the process. For example, when QQ.exe is running, many subtasks are running at the same time.

1.2 The difference between process and thread

1.2.1 Scheduling: The thread is the basic unit of scheduling and allocation, and the process is the basic unit of owning resources.

1.2.2 Concurrency: Not only can processes be executed concurrently, but also between multiple threads of the same process.

1.2.3 Owning resources: A process is an independent unit that owns resources. The thread itself basically does not own system resources, only a few essential resources (such as a program counter, a set of registers, and a stack) in operation, but it can Share all resources owned by the process with other threads belonging to the same process. The address space cannot be shared between processes, and threads share the address space of the process in which they are located.

1.2.4 System overhead: When creating or canceling a process, because the system must allocate and reclaim resources for it, the overhead of the system is significantly greater than that of creating or canceling threads.

1.3 What is multithreading?

Multithreading means executing multiple threads almost simultaneously.

1.4 Why use multithreading

1.4.1 The use of threads can put tasks in the program that occupy a long time in the background for processing.

1.4.2 The user interface is more attractive. For example, if the user clicks a button to trigger the processing of an event, a progress bar can pop up to show the progress of the processing.

1.4.3 The operating efficiency of the program may be improved.

1.4.4 In the realization of some waiting tasks, such as user input, file reading and network sending and receiving data, threads are more useful.

2 Thread status

Generally speaking, a thread includes the following states: created (new), ready (runnable), running (running), blocked (blocked), timed_waiting, waiting, and dead (dead).



3 How to use multithreading

3.1 Inherit the Thread class



You can see that the program is executed alternately, but it will eventually execute to 98.

3.2 Implement the Runnable interface




You can still see that the program is executed alternately, but it will eventually execute to 98.

3.3 Inherit and implement the Callable interface



What needs to be explained here is that to implement the Callable interface, the call() method must be rewritten, and the FutureTask class must be used. I will not introduce it here. I will update the thread pool later and talk about it in detail.

3.4 Use thread pool such as Executor framework

This part will not be introduced for the time being. Update thread pool and Executor will be introduced in detail later.

4 Is it necessarily faster to use multithreading?

Answer: Not necessarily, because multi-threading will perform context switching, and context switching will bring overhead.

4.1 What is context switching?

For a single-core CPU, the CPU can only run one thread at a time. When one thread is running, it switches to another thread. This is called thread context switching (similar to processes). During thread context switching, data such as the program counter and CPU register status are recorded.

4.2 How to reduce context switching?

4.2.1 Reduce the number of threads

Since a CPU can only execute one thread at a time, and we want to make the program execute concurrently, the operating system has to constantly switch context to make us feel that the program is executed concurrently from the senses. Therefore, as long as we reduce the number of threads, we can reduce the number of context switches.

However, if the number of threads is already less than the number of CPU cores, each CPU executes one thread. In principle, the CPU does not need to perform context switching, but this is not the case.

4.2.2 Control the number of threads on the same lock

If multiple threads share the same lock, when one thread acquires the lock, other threads will be blocked; when the thread releases the lock, the operating system will select one of the blocked threads for execution, and context switching will occur again.

Therefore, reducing the number of threads on the same lock can also reduce the number of context switches.

4.2.3 Using lock-free concurrent programming

Tasks that need to be executed concurrently are stateless: HASH segmentation

The so-called stateless means that concurrently executed tasks have no shared variables, and they are all executed independently. For this type of task, HASH segmentation can be performed according to ID, and each segment is executed by a thread.

Tasks that need to be executed concurrently are stateful: CAS algorithm

If the task needs to modify the shared variables, then the execution order of the threads must be controlled, otherwise security issues will occur. You can lock the task to ensure the atomicity and visibility of the task, but this will cause blocking and thus context switching; in order to avoid context switching, you can use the CAS algorithm, which is only used when the shared variable needs to be updated within the thread To update, this method will not block the thread and ensure the safety of the update process.


5 Disadvantages of using multithreading:

5.1 The overhead of context switching

When the CPU switches from executing one thread to executing another thread, it needs to store the local data of the current thread, program pointers, etc., and then load the local data of another thread, program pointers, etc., before finally starting to execute. This kind of switching is called "context switching." The CPU will execute a thread in one context, and then switch to another context to execute another thread. Context switching is not cheap. If it is not necessary, the occurrence of context switching should be reduced.

5.2 Increase resource consumption

When a thread is running, it needs to get some resources from the computer. In addition to the CPU, the thread also needs some memory to maintain its local stack. It also needs to take up some resources in the operating system to manage threads.

5.3 Programming is more complicated

When accessing shared data in multiple threads, thread safety must be considered.

6 thread safety

6.1 Definition of thread safety

A class is thread-safe when it can be called safely by multiple threads.

6.2 Thread Safety Classification

Thread safety is not an all-or-nothing proposition. Shared data can be divided into the following five categories in the order of the degree of security:

Immutable, absolute thread safety, relative thread safety, thread compatibility and thread opposition.

6.2.1. Immutable

Immutable objects must be thread-safe. No matter the method implementation of the object or the caller of the method, there is no need to take any thread-safety measures. As long as an immutable object is constructed correctly, then Its external visible state will never change, and it will never be seen in an inconsistent state among multiple threads.

Immutable types: basic data types modified by the final keyword; String; enumeration types ; some subclasses of Number, such as Long and Double numeric packaging types, BigInteger and BigDecimal and other big data types. But the atomic classes AtomicInteger and AtomicLong, both of which are subtypes of Number, are not immutable.

6.2.2 Absolute thread safety

Regardless of the runtime environment, the caller does not need any additional synchronization measures.

6.2.3 Relative thread safety

Relative thread safety needs to ensure that the individual operations on this object are thread-safe, and no additional safeguards are needed when calling, but for some consecutive calls in a specific order, additional synchronization methods may need to be used on the calling side. Ensure the correctness of the call.

6.2.4 Thread compatibility

Thread compatibility means that the object itself is not thread-safe, but you can ensure that the object can be used safely in a concurrent environment by correctly using synchronization on the calling side. We usually say that a class is not thread-safe. Most of the time, it means This is the case. Most of the classes in the Java API are thread-compatible, such as the collection classes ArrayList and HashMap corresponding to the previous Vector and HashTable.

6.2.5 Thread opposition

Thread opposition refers to code that cannot be used concurrently in a multi-threaded environment regardless of whether the caller has taken synchronization measures. Because the Java language is inherently multi-threaded, thread opposition, such as multi-thread-repelling code, rarely occurs, and is usually harmful, and should be avoided as much as possible.