Multithreading—understanding threads

Article directory

  • What is a process?
    • How to manage processes?
    • Know PCB
    • Understand the process of process scheduling
    • virtual address space
  • What is a thread?
  • Process VS Thread
  • Properties and methods of Thread class
    • Properties of Thread class
    • Thread class methods
      • Construction method
      • Ordinary method
  • Thread status

What is a process?

Process, also called “task”, a running program is a process. In other words, a process is a running program. At the same time, there are many processes in the operating system. How are they managed?

How to manage processes?

The management process actually does two things:

  1. Describe the process: Describe in detail what attributes and information a process has? This is achieved through a structure, which contains various information about the process. This structure is called PCB (Process Control Block);
  2. Organization process: Connect several PCBs through a data structure, so that they can be added, modified, and deleted. This data structure generally uses a doubly linked list.

Note:

  1. Creating a process actually creates a PCB and then puts it into a doubly linked list.
  2. Ending the process actually means finding the node in the doubly linked list and then deleting the node.
  3. Viewing the process list actually traverses the entire doubly linked list.
  4. A process may be one PCB, or it may be multiple.

Understand PCB

To understand PCB is to understand what information is contained in PCB:

  • pid: is the identity of a process

    On the same host and at the same time, the pids of these processes are unique. Distinguish processes by pid.

  • Memory pointer: describes the memory resources held by the process

    When we double-click an executable file, the operating system loads the core data of the file into memory and creates a process PCB in the memory. This will allocate a certain amount of memory space to the process. This memory space will be divided into different areas. The memory pointer is used to describe what each area is used for.

  • File descriptor table: describes the file resources held by the process

    Each process can open some files (data stored on the hard disk), and the file descriptor table records which files are currently opened by this process.

  • Process status: Describes whether the process can currently be called

    Ready state: The process can be scheduled to be executed on the CPU.
    Blocking state: The process cannot be scheduled for execution on the CPU.

  • Process priority: Describes the order in which processes are called

    When creating a process, priorities can be interfered with through some system calls.

  • Process context: Saves the intermediate results generated during the execution of the current process.

    After a process is executed on the CPU for a while, it will switch to another process for execution. After a period of time, it may switch back again to continue execution. Then you need to know where the last execution was. The process context is used to save intermediate results.

  • Process accounting information: Count how long a process has been executed on the CPU

    When a process is executed, the process priority controls which process is executed, but this may cause a certain process to never be executed. By counting process accounting information, process scheduling can be more balanced and avoid failure to execute a certain process.

Understand the process of process scheduling

Among them, process status, process priority, process context and process accounting information are all information related to process scheduling. So what is process scheduling?

We must first understand: The process is the basic unit of resource allocation by the operating system.

Process scheduling is actually performed in a “parallel” + “concurrency” manner.

Parallelism means that each CPU core can run a process independently, and multiple CPU cores can run multiple processes at the same time.

Concurrency, that is: on a CPU core, first run process 1, then run process 2, then run process 3, then run process 1… and so on. As long as the switching speed is fast enough, macroscopically it looks like the three processes are running at the same time.

The purpose of process status, process priority, process context and process accounting information is to support “process scheduling”

Virtual address space

Virtual address space is also a very critical concept in the process.

We know that when a process is created, a certain amount of memory space is allocated to each process to complete the work of the process. Right now:

Under normal circumstances, each process uses its own memory without any problems. But if a process uses a wild pointer, it accidentally accesses the memory of another process and modifies it. This is a big problem: it not only affects its own execution, but also affects the execution of others. We use virtual address space to avoid this problem.

We use “virtual address space” to allow each process to have its own memory space and isolate it from the memory space of other processes. When a process wants to access memory, it uses the MMU device to map the virtual memory space to the real memory space and access the real memory. If it is found that the memory accessed by a process is out of bounds, the MMU device will intercept it and close the process to prevent it from affecting other processes.

In the face of some scenarios where multiple processes need to cooperate, an inter-process communication mechanism is introduced. Its principle is: find a common resource that all processes can access, and then exchange data based on the common resource.

What is a thread?

Although multi-process has implemented concurrent programming, there is a huge problem: if processes are frequently created and destroyed, this operation will be relatively inefficient.

The process of creating a process: 1. Create PCB 2. Allocate resources to the process and assign values to the PCB 3. Insert the PCB into the linked list
The process of destroying the process: 1. Delete the PCB from the linked list 2. Release the resources held by the PCB 3. Destroy the PCB

Among them, allocating resources and releasing resources requires a lot of work for the operating system and takes a lot of time.

Therefore, programmers invented “threads”. A process has at least one thread by default and may have multiple threads. These threads can be scheduled individually on the CPU. The most important thing is: these threads in the same process share the same system resources (memory + files), and the cost of creating and destroying threads is much less than that of the process. Therefore, threads are also called “lightweight processes”.

As mentioned earlier, the operating system describes the process through PCB. More accurately, it describes the process through a set of PCBs.
Each PCB corresponds to a thread, and a process may contain multiple threads.

There are some advantages to using multithreading:

  1. Ability to make full use of multi-core CPUs and improve efficiency.
  2. Only when creating the first thread, you need to apply for resources. Subsequently creating new threads will share the same resources, saving the overhead of applying for resources; when destroying threads, resources will only be released when the last thread is destroyed. Saves the overhead of releasing resources.

There are also some problems with using multi-threading:

  1. The more threads, the better. When the CPU core is already saturated, continuing to add threads will not improve efficiency. On the contrary, because there are too many threads, the thread scheduling overhead is too high, which affects the efficiency.
  2. Threads may affect each other, causing thread safety issues
  3. If an accident occurs in one thread, the entire process may crash.

Process VS Thread

  1. Process contains threads
  2. Threads are lighter than processes, faster to create, and faster to destroy
  3. Multiple threads of the same process share the same system resources (memory + files), and processes have their own system resources (memory + files).
  4. Process is the basic unit of resource allocation, and thread is the basic unit of scheduling execution.

Attributes and methods of Thread class

Attributes of Thread class

public class Test {<!-- -->
    public static void main(String[] args) {<!-- -->
        Thread thread = new Thread();
        System.out.println(thread.getId());
        System.out.println(thread.getName());
        System.out.println(thread.getState());
        System.out.println(thread.getPriority());
        System.out.println(thread.isDaemon());
        System.out.println(thread.isAlive());
        System.out.println(thread.isInterrupted());

// 20
//Thread-0
// NEW
// 5
// false
// false
// false

    }
}
  1. The id is the unique identifier of the thread, and different thread ids will not be repeated.
  2. The name will be used when debugging. You can define it in the thread’s construction method.
  3. state represents the current state of the thread (described below)
  4. priority is used when thread scheduling
  5. daemon: daemon thread, also called background thread. Foreground process: will prevent the process from exiting. If the foreground thread has not finished executing after the main thread has finished executing, it will wait for the foreground thread to finish executing before exiting the process; background process: will not organize the exit of the process and will exit when the main thread finishes executing. process. The thread we create is a foreground thread by default.
  6. alive: Determine whether the kernel thread is alive. When new comes out of the Thread object but is not started using the start method, the thread will not be put into the kernel. Only after using the start method will the thread be put into the kernel for execution; when the thread completes the task in the kernel, it will exit the kernel and clear the kernel. thread, but the Thread object is still there.
  7. Interrupt: Thread interrupts, allowing the thread to end early. The essence is to let the run method end as soon as possible, instead of letting the run method exit halfway through execution. Interrupt has two situations: 1. If the thread is executing, set the flag bit to true to interrupt the thread. 2. If the thread is blocked, wake up from sleep and throw an exception. It will be caught by catch and processed in catch. There are two a way of interruption.
  • Use the tag bits that come with the thread library
 //By using the tag bits that come with the standard library
    public static void main(String[] args) {<!-- -->
        Thread t = new Thread(() -> {<!-- -->
            while (!Thread.currentThread().isInterrupted()) {<!-- -->
                System.out.println("hello thread");
                try {<!-- -->
                    Thread.sleep(1000);
                } catch (InterruptedException e) {<!-- -->
                    // e.printStackTrace();

                    // [Method 1] End the thread immediately
                    break;

                    //[Method 2] Do nothing, ignore it. The thread continues to execute

                    // [Method 3] The thread will be processed later
                    // Thread.sleep(1000);
                    // break;
                }
            }
            System.out.println("t thread has finished executing");
        });

        t.start();

        try {<!-- -->
            Thread.sleep(3000);
        } catch (InterruptedException e) {<!-- -->
            e.printStackTrace();
        }

        t.interrupt();
        System.out.println("Set to let t thread end!");
    }
  • Customize a mark position
//Set a mark bit yourself to interrupt and exit
    public static boolean isQuit = false;

    public static void main(String[] args) {<!-- -->
        Thread thread = new Thread(() -> {<!-- -->
            while (!isQuit){<!-- -->
                System.out.println("Execution thread");
                try {<!-- -->
                    Thread.sleep(1000);
                } catch (InterruptedException e) {<!-- -->
                    e.printStackTrace();
                }
            }
        });
        thread.start();

        while (true){<!-- -->
            System.out.println("Execute main");
            isQuit = true;
            System.out.println("Manually interrupt the thread");
            try {<!-- -->
                Thread.sleep(5000);
            } catch (InterruptedException e) {<!-- -->
                e.printStackTrace();
            }
        }
    }

Methods of Thread class

Construction method


The construction method is mainly used to create threads. There are currently seven ways to create threads. Click to view

You can add a String type parameter in the constructor to name the thread.

Common methods

  • start(): Start the thread. Put the thread into the kernel for execution.

  • interrupt(): interrupt the thread. Let the thread exit early.

  • join(): Thread waits. Calling join() in main means waiting for the thread to finish executing before executing the main thread.

  • sleep(): Thread sleeps. Let the thread block for a while.

    PCB has two queues when managing threads: a ready queue and a blocking queue. Calling sleep() is to put the thread in the blocking queue, wait for the blocking time to end, and then put it back into the ready queue to participate in scheduling.

Thread status

Threads have a total of six states, which we can understand like this: