01. Basic knowledge of threads

1. Processes and threads

The origin of processes and threads:

The working principle of computer cpu, the operating system is actually switching tasks constantly when scheduling a single-core cpu, because the cpu can only do one thing at the same time, and the cpu is very fast, and often needs to wait for other parts when completing a task To complete tasks (such as hard disk read and write i/o blocking), the CPU can only switch back and forth between different tasks in order to maximize resources at this time, which is what we call concurrency.

But concurrency has brought some problems. The original single task has now become multiple tasks. How to switch between tasks? At this time, the concept of process is introduced.

Use processes to correspond to a program, each process corresponds to a certain memory address space, and can only use its own memory space, and each process does not interfere with each other. And the process saves the running state of the program at each moment , which provides the possibility for process switching.

When the process is temporary, it will save the current process state (such as process ID, resources used by the process, etc.), and when it switches back the next time, it will restore according to the previously saved state, and then continue to execute. This is concurrency, which allows the operating system to look at the macroscopically that multiple tasks are executing in the same time period.

In other words, the process makes the concurrency of the operating system possible.

The thread is a subset of the process, and the thread is also the smallest execution unit of an application.

The emergence of processes solves the concurrency problem of the operating system, but for a process, it also needs to perform multiple sub-tasks, such as reading, computing, and real-time display. In order to maximize efficiency, people introduce the concept of threads. . The process makes the concurrency of the operating system possible, and the thread makes the internal concurrency of the process possible.

Note that although a process includes multiple threads, these threads share the resources and address space occupied by the process. The process is the basic unit of the operating system for resource allocation, and the thread is the basic unit of the operating system for scheduling.

2. The difference between process and thread

  1. Thread is the smallest unit of program execution, and process is the smallest unit of operating system allocation of resources;

  2. A process consists of one or more threads, which are different execution routes of code in a process

  3. The processes are independent of each other, but each thread in the same process shares the memory space of the program (including code segments, data sets, heaps, etc.) and some process-level resources (such as open files and signals, etc.). The thread is not visible in other processes;

  4. Scheduling and switching: thread context switching is much faster than process context switching

An example in life: When you are online, you hang up QQ, look at iQiyi, and use Thunder to download files. At this time, QQ, iQiyi, and Xunlei can be considered as a process. And you can chat with Zhang San, Li Si, etc. at the same time in QQ, then the chat interface of Zhang San, Li Si, etc. is a thread.

Question: Is the performance of multithreading necessarily due to single threading?

Not necessarily, it depends on the specific task and the configuration of the computer. For example: For a single-core CPU, if it is a CPU-intensive task, such as decompressing files, the performance of multi-threading is not as good as that of single-threading.

Because decompressing files needs to occupy CPU resources all the time, if multi-threading is used, the overhead caused by thread switching will actually degrade performance. However, for tasks such as interactive tasks, multi-threading must be used. For multi-core CPUs, multi-threading is definitely better than single-threading for decompressing files, because multiple threads can more fully utilize the resources of each core.

Although multi-threading can improve program performance, its programming is much more complicated than single-threaded, and thread safety issues must be considered.

Therefore, in the actual programming process, the specific selection should be made according to the actual situation.

3. Thread creation method in java

In the previous article, we have a preliminary understanding of the concept of threads, so how can we create a thread in the JVM?

There are generally two ways to create a thread in java: 1) inherit the Thread class 2) implement the Runnable interface

  • 1. Create a thread by inheriting the Thread class

    /**
     * 1 .继承Thread类
     *   继承Thread类的话,必须重写run方法,在run方法中定义需要执行的任务。
     **/
    class ThreadOne extends Thread{
          
          
        @Override
        public void run(){
          
          
            System.out.println(Thread.currentThread().getId()+"开始运行			了");
        }
    }
    
    /**
     *  创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线 程。
     **/
    public static void main(String[] args) throws Exception{
          
          
        new ThreadOne().start();
        //jdk1.8下用 lambda表达式简化写法
        new Thread(()-> System.out.println(Thread.currentThread().getId()+"开始运行		了")).start();
    }	
    

After the thread class is created, the thread is just a different java object at this time, and it cannot be called a thread. The thread can only be started by calling the start() method. Note: instead of calling the run() method to start the thread, the run method only defines the tasks that need to be executed. If the run method is called, it is equivalent to executing the run() method in the ThreadOne object in the main thread, and there is nothing to do with ordinary method calls. The difference is that at this time, a new thread is not created to perform the defined task.

  • Create a thread by implementing the Runnable interface
    Java defines a Runnable interface for us, we can achieve the creation of a thread by implementing the interface and rewriting the run() method in the interface
//自定义一个Runnable接口的实现类,并重写其中run方法
class ThreadTwo implements Runnable{
    
    
    @Override
    public void run(){
    
    
        System.out.println(Thread.currentThread().getId()+"开始运行了");
    }
}
public static void main(String[] args) throws Exception{
    
    
    //创建Thread类并将接口实现做入参,并调用start方法
    new Thread(new ThreadTwo()).start();
}	

Looking at the jdk source code, we can find that the Runnable interface structure is very simple. It just defines a run method with no parameters and no return value.
Insert picture description here

So in a strict sense, creating a thread by implementing the Runnable interface is a kind of imprecise calligraphy. In jdk, only the Thread class represents the thread. The execution unit of the thread is the run method. Check the Thread.run source code.

Insert picture description here

We can find that the run method just executes the run method in the target, and what is the target. Looking at the constructor of Thread, we can find:

Insert picture description here

The target here is the implementation of the Runnable interface we passed in. The instance of Runnable only provides the logic of his execution unit for the thread.

So there are two ways to create a thread. The first is to create a Thread implementation class, and the second is to implement the Runnable interface. This statement is not rigorous.

Strictly speaking, the only way to create a thread is to construct the Thread class, and there are two ways to implement the execution unit of the thread: the first is to rewrite the run method of Thread, and the second is to implement the run of the Runnable interface. Method, and use an instance of the Runnable interface as an input parameter and construct a Thread instance.

4. Detailed explanation of thread life cycle

As the smallest unit of program execution, thread also has a complete life cycle:

Insert picture description here

As shown in the figure above, the life cycle of a thread is roughly divided into the following five parts:

NEW, RUNNABLE, RUNING, BLOCKED, TERMINATED

The following is a detailed explanation of each state

  • NEW status

    When we create a thread object, such as new Thread(), the thread has not yet started calling the start method to start, then the thread at this time is in the NEW state. In fact, to be precise, the thread does not exist at this time. Before calling the start() method, you just created an ordinary Java object with the keyword new.

    The NEW state can be entered into the RUNNABLE state by start().

  • RUNNABLE state

    After the thread object is created, if you want to change to the RUNNABLE state, you must call the start() method. After calling this method, a thread can be truly created in the JVM process. But once the thread is started, it will not be executed immediately. The running of the thread depends on the scheduling of the CPU like the process. We call this state of waiting for execution as the RUNNABLE (runnable) state, which means it has Qualified for execution, but it hasn’t really been executed yet.

    Since the thread is not in the Running state at this time, the thread in this state will not directly enter the BLOCKED and TERMINATED state. Even if wait, sleep or other block IO operations are called during the execution of the thread, the thread must have obtained the CPU. The scheduling execution right can change the state.

    RUNNABLE threads can only terminate unexpectedly or enter the RUNNING state.

  • RUNNING state

    When the CPU selects a thread from the task executable queue through polling or other methods, it can truly execute its own internal logic code at this time, and the state of the thread at this time is the RUNING state. It can be said that a thread in the RUNNING state is actually RUNNABLE, but the converse is not true.

    Common transitions of thread state in RUNNING state:

    • RUNNING becomes BLOCK state

      1. Called sleep and wait methods and entered waitSet.

      2. Perform a blocking IO operation, such as reading and writing network data

      3. In order to obtain a certain lock resource, it is added to the blocking queue of the lock

    • RUNNING becomes RUNNABLE state

      1. The CPU scheduler polling caused the thread to give up execution

      2. The thread actively called the yield method and gave up the execution right of the CPU

    • RUNNING becomes TERMINATED state

      1. Call the stop method

  • BLOCKED state

    The thread enters the blocked state for some reason. For details, see the state switch that the thread can perform in the BLOCKED state:

    • Enter RUNNABLE state

      1. The thread has finished sleeping for the specified time

      2. The end of the thread blocking operation, such as reading the data you want to get

      3. The thread in Wait is awakened by other threads notify/notifyall

      4. The thread has acquired a certain lock resource

      5. The thread is interrupted in the blocking process, for example, other threads call the interrupt method

    • Enter TERMINATED state

      Call stop or unexpected death (JVM Crash)

  • TERMINATED status

    The final state of the thread. The thread in this state will not undergo state changes, which means that the entire life cycle is over. The thread enters the TERMINATED state:

    1. The thread runs normally and ends its life cycle

    2. The thread running error ended unexpectedly

    3. JVM Crash, causing all threads to end

It is very important to understand the transition between the various states of the life cycle of a thread. The state enumeration specifically defined by each language may be different, but in general they will fall within the scope of these five states.

Guess you like

Origin blog.csdn.net/weixin_43828467/article/details/110141187