1. Thread overview
Threads are the basic execution unit for program execution. When the operating system (excluding single-threaded operating systems, such as Microsoft's early DOS) executes a program, a process will be established in the system, and in this process, at least one thread must be established (this thread is called the main thread). thread) as the entry point for this program to run. Therefore, any program running in the operating system has at least one main thread.
Processes and threads are two essential operating models in modern operating systems. There can be multiple processes in the operating system, including system processes (processes created internally by the operating system) and user processes (processes created by user programs); a process can have one or more threads. There is no shared memory between processes, which means that the processes in the system run in their own independent memory spaces. Threads in a process can share the memory space allocated by the system to the process.
Threads can not only share the memory of the process, but also have a memory space of their own. This memory space is also called the thread stack. It is allocated by the system when the thread is created. It is mainly used to save the data used inside the thread, such as thread stack. Variables defined in the execution function.
Note: Any thread will execute a function when it is created. This function is called the thread execution function. This function can also be regarded as the entry point of the thread (similar to the main function in the program). No matter what language or technology is used to create a thread, this function must be executed (the expression of this function may be different, but there will always be such a function). For example, the third parameter of the API function CreateThread used to create a thread in Windows is the pointer of this execution function.
After the operating system divides the process into multiple threads, these threads can be executed concurrently under the management of the operating system, thus greatly improving the running efficiency of the program. Although the execution of threads looks like multiple threads executing at the same time from a macro perspective, in fact this is just a cover-up of the operating system. Since a CPU can only execute one instruction at a time, it is impossible to execute two tasks at the same time on a computer with one CPU. In order to improve the running efficiency of the program, the operating system will remove a thread when it is idle and let other threads execute it. This method is called thread scheduling. The reason why we see multiple threads executing at the same time on the surface is because the time for switching between different threads is very short, and under normal circumstances switching is very frequent. Suppose we have threads A and B. At run time, it may be that after A executes for 1 millisecond, after switching to B, B executes for another 1 millisecond, then switches to A, and A executes for another 1 millisecond. Since 1 millisecond is difficult for ordinary people to perceive, on the surface it looks like A and B are executed at the same time, but in fact A and B are executed alternately.
2. The benefits that threads bring to us
Proper use of threads can reduce development and maintenance costs and even improve the performance of complex applications. For example, in GUI applications, events can be better processed through the asynchronous nature of threads; in application server programs, multiple threads can be established to handle client requests. Threads can even simplify the implementation of virtual machines. For example, the garbage collector of the Java Virtual Machine (JVM) usually runs in one or more threads. Therefore, using threads will improve our applications in the following five aspects:
1. Make full use of CPU resources
Most computers in the world now have only one CPU. Therefore, it is particularly important to make full use of CPU resources. When executing a single-threaded program, the CPU may be idle while the program is blocked. This will cause a lot of waste of computing resources. Using multi-threading in a program can run other threads when a thread is sleeping or blocked and the CPU happens to be idle. In this way, it is difficult for the CPU to be idle. Therefore, CPU resources are fully utilized.
2. Simplify the programming model
If the program only completes one task, then just write a single-threaded program and write the code according to the steps to perform the task. But to complete multiple tasks, if you still use a single thread, you have to determine in the program whether and when each task should be executed. For example, it displays the hours, minutes, and seconds of a clock. If you use a single thread, you have to judge the rotation time and angle of these three pointers one by one in the loop. If three threads are used to handle the display of these three pointers, then it means performing a separate task for each thread. This helps developers understand and maintain the program.
3. Simplify the processing of asynchronous events
When a server application receives different client connections, the simplest way to handle it is to create a thread for each client connection. The listening thread is then still responsible for listening for requests from the client. If this kind of application is processed by a single thread, when the listening thread receives a client request, it starts to read the data sent by the client. After reading the data, the read method is blocked, that is to say, this thread will Can no longer listen to client requests. If you want to handle multiple client requests in a single thread, you must use non-blocking Socket connections and asynchronous I/O. However, using asynchronous I/O is more difficult to control and more error-prone than using synchronous I/O. Therefore, asynchronous events like multiple requests can be handled more easily using multithreading and synchronous I/O.
4. Make the GUI more efficient
When using a single thread to process GUI events, a loop must be used to scan for GUI events that may occur at any time. Within the loop, in addition to scanning for GUI events, other program codes must be executed. If the code is too long, the GUI events will be "frozen" until the code is executed.
In modern GUI frameworks (such as SWING, AWT and SWT), a separate event dispatch thread (EDT) is used to scan GUI events. When we press a button, the button's click event function will be called in this event dispatch thread. Since the task of EDT is only to scan GUI events, the response to events in this way is very fast.
5. Cost savings
There are generally three methods to improve program execution efficiency:
(1) Increase the number of CPUs in the computer.
(2) Start multiple processes for one program
(3) Use multiple processes in the program.
The first method is the easiest to do, but it is also the most expensive. This method does not require modification of the program. In theory, any program can use this method to improve execution efficiency. Although the second method does not require the purchase of new hardware, it is not easy to share data. If the task to be completed by this program requires sharing data, this method is not convenient, and starting multiple threads will consume a lot of system resources. The third method just makes up for the shortcomings of the first method, while inheriting their advantages. In other words, there is no need to purchase a CPU, nor will it occupy a lot of system resources by starting too many threads (by default, the memory space occupied by a thread is much smaller than the memory space occupied by a process). Multiple), and multi-threading can simulate the running mode of multiple CPUs. Therefore, using multi-threading is the cheapest way to improve program execution efficiency.
3. Java thread model
Since Java is a purely object-oriented language, Java's threading model is also object-oriented. Java encapsulates all the necessary functions of threads through the Thread class. To create a thread, there must be a thread execution function. This thread execution function corresponds to the run method of the Thread class. The Thread class also has a start method, which is responsible for establishing a thread, which is equivalent to calling the Windows thread creation function CreateThread. When the start method is called, if the thread is successfully established, the run method of the Thread class is automatically called. Therefore, any Java class that inherits Thread can create a thread through the start method of the Thread class. If you want to run your own thread execution function, you must override the run method of the Thread class.
In addition to the Thread class in the Java thread model, there is also an interface Runnable that identifies whether a Java class can be used as a thread class. This interface has only one abstract method run, which is the thread execution function of the Java thread model. Therefore, the only criterion for a thread class is whether the class implements the run method of the Runnable interface. In other words, a class with a thread execution function is a thread class.
As can be seen from the above, there are two ways to create threads in Java. One is to inherit the Thread class, and the other is to implement the Runnable interface and create threads through Thread and the class that implements Runnable. In fact, these two methods are essentially It is said to be a method, that is, the thread is created through the Thread class and the run method is run. But their big difference is that threads are created by inheriting the Thread class. Although it is easier to implement, since Java does not support multiple inheritance, if this thread class inherits Thread, it cannot inherit other classes. Therefore, The Java threading model provides methods to establish threads by implementing the Runnable interface, so that thread classes can inherit business-related classes instead of the Thread class when necessary.