The creation and use of threads in Java, common methods of the Thread class

1. What are processes and threads

1.1 Meaning

1.1.1 Process

A process is an instance of a program that is running. In an operating system, a process represents an executing program, which includes program code, data, and system resources required for program execution.

The most intuitive is our task manager:

Every application in Task Manager is a process.

1.1.2 Threads

A thread is a unit of execution in a process. A process can contain multiple threads, each thread has an independent execution path, and can independently perform specific tasks, and all threads in a process share the resources of this process.

Such as the following example. Process: The entire operating process of a restaurant can be regarded as a process. It includes all resources and activities such as kitchen, restaurant hall, waiters, cooks, customers, etc. Thread: In a restaurant, a waiter can be thought of as a thread. There are many waiters, who are responsible for receiving customers, recording orders, passing menus, serving dishes and other tasks. The waiter is an execution unit in the restaurant process, and he shares the same resources with other waiters, such as the table, kitchen, ingredients, etc. of the restaurant. Multiple servers can execute tasks in parallel to improve efficiency.

1.1.3 The difference between thread and process

The birth of the process is to realize the concurrent execution of the program. The so-called concurrency means that in the operating system, there are several programs in a period of time between being started and running and running, and these programs are all running on the same processor.

The purpose of the birth of threads is to reduce the time and space overhead of programs during concurrent execution, so that the operating system has better concurrency.

  • Address space : Threads share the address space of the process, while processes have independent address spaces.
  • Resources : Threads share the resources of the process, such as memory, I/O, CPU, etc., while the resources between processes are independent.
  • Robustness : Multi-process is stronger than multi-thread. After a process crashes, it will not affect other processes in protected mode, but if a thread crashes, the whole process will die.
  • Execution process : Each independent process has a program running entry, sequential execution sequence and program exit, and the execution overhead is high. However, threads cannot be executed independently, and must depend on the application program. The application program provides multiple thread execution control, and the execution overhead is small.
  • Concurrency : Both can be executed concurrently.
  • When switching : When the process is switched, it consumes a lot of resources and has low efficiency. So when it comes to frequent switching, it is better to use threads than processes.
  • Others : Process is the basic unit of operating system resource allocation , and thread is the basic unit of operating system scheduling .

1.2 PCB and TCB

Process control block PCB (Process Control Block), which is a data structure used to describe and control the operation of the process . It is the only sign of the existence of the process, and it is also an important basis for the operating system to manage the process.

TCB (Thread Control Block) is the abbreviation of Thread Control Block, which is a data structure used in the operating system to describe and control the running of threads .

Their relationship:

Process identifiers, statuses, priorities, registers, program counters, stack pointers, resource lists, synchronization and communication mechanisms, and other information are stored in the PCB. The PCB is the only sign of the existence of a process, and it is also the basis for realizing process switching and scheduling.

The TCB is similar to the PCB. The TCB also stores thread identifiers, status, priority, registers, program counters, stack pointers, signal shielding, and other information. TCB is the only sign of thread existence, and it is also the basis for thread switching and scheduling.

2. Several ways to create threads

Thread is a concept in the operating system. The operating system kernel implements such a mechanism as threads, and provides some APIs to the user layer for users to use. The Thread class in the Java standard library can be regarded as a further abstraction and encapsulation of the API provided by the operating system.

2.1 Inheriting the Thread class

(1) Inherit Thread to create a thread class.

class MyThread extends Thread{
    //重写run方法,run 表示这个线程需要执行的任务
    @Override
    public void run() {
        System.out.println("这个线程需要执行的任务");
    }
}

(2) Create an instance of MyThread.

//创建一个 MyThread 实例
MyThread myThread = new MyThread();

(3) Call the start() method, and the thread starts to execute.

//start 方法表示这个线程开始执行,注意,这里不是调用 run()方法
myThread.start();

2.2 Implement the Runnable interface

(1) Implement the Runnable interface.

//通过 实现 Runnable 接口来创建一个线程
class MyRunnable implements Runnable{

    //需要重写run方法
    @Override
    public void run() {
        System.out.println("这个线程需要执行的任务");
    }
}

(2) Create a Thread instance.

//创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为参数。
Thread thread = new Thread(new MyRunnable());

(3) Call the start() method, and the thread starts to execute.

//线程开始运行
thread.start();

2.3 Other deformations

  • An anonymous inner class creates a Thread subclass object.
Thread thread1 = new Thread(){
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Thread 子类对象");
    }
};
  • Anonymous inner classes create Runnable subclass objects.
Thread thread2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Runnable 子类对象");
    }
});
  • lambda expressions create Runnable subclass objects.
Thread thread3 = new Thread(()-> {
    System.out.println("使用匿名类创建 Thread 子类对象");
});

3. Common methods of the Thread class

3.1 Common construction methods of Thread

method illustrate
Thread() create thread object
Thread(Runnable target) Create a thread object using a Runnable object
Thread(String name) Create a thread object and name it
Thread(Runnable target, String name) Use the Runnable object to create a thread object and name it
Thread thread = new Thread("小明"){
    @Override
    public void run(){
        while(true){
        	System.out.println("我是小明");
    	}   
    }
};

The name of this thread is "Xiao Ming", how to check the name of this thread? In the Java Development Kit (JDK), you can use jconsole to monitor threads.

  1. Open your jdk path, here is C:\Program Files\Java\jdk1.8.0_31\bin, open the jconsole tool under this path.
  2. Run the above code, and then proceed as follows:

You can see that there is a thread named "Xiao Ming", which is the thread we created.

3.2 Method to get Thread attribute

Attributes access method
ID getId()
name getName()
state getState()
priority getPriority()
Whether background thread isDaemon()
whether to survive isAlive()
Is it interrupted isInterrupted()
  • ID is the unique identifier of the thread, different threads will not be repeated.
  • name is the name of the thread.
  • The state represents a situation that the thread is currently in, such as blocking, running, and so on.
  • Threads with higher priority are more likely to be scheduled.
  • About the background thread: Daemon Thread is a thread that provides services in the background during the running of the program. In contrast to foreground threads (also known as user threads), background threads do not prevent the termination of the program. Background threads are automatically terminated when all foreground threads (user threads) end, even if they haven't finished executing yet.
  • Whether it is alive, that is, a simple understanding, is whether the run method is finished.

3.3 The method to start the thread start()

It has been demonstrated in the previous article, and we have seen how to create a thread object by overriding the run method, but the creation of a thread object does not mean that the thread starts running. Override the run method to provide the thread with a list of instructions for what to do. Only by calling the start() method can the thread execute independently, and only by calling the start method can a thread be created at the bottom of the operating system.

3.4 thread sleep sleep()

method illustrate
public static void sleep(long millis) throws InterruptedException Sleep the current thread for millis milliseconds
public static void sleep(long millis, int nanos) throws InterruptedException Can sleep with higher precision

The following code:

public static void main(String[] args) throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread.sleep(3000);//睡眠当前线程
    long end = System.currentTimeMillis();
    System.out.println(end - begin);
}

result:

Some of its more detailed content is introduced later.

3.5 Interrupting threads

The interruption here does not mean immediate interruption. Let's look at the following case for details.

There are two threads here, one thread is used to interrupt the other thread, and the Thread method is not used here.

public class Main {
    
    public static volatile boolean flag = true;
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(flag){
                    try {
                        Thread.sleep(500);//睡眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        //中断线程
        Thread.sleep(3000); //睡眠 3 秒
        flag = false;//更改标记
        System.out.println("开始中断线程");
    }
}

(The role of volatile will not be introduced here, and will continue to be updated later.)

result:

You will find that the interrupt here is to change the value of the flag bit, and there is a delay here, and the thread can only be terminated when the sleep sleep ends.

So you can use the method provided by Thread, namely: isInterrupted() or interrupted(), their advantage is that they can wake up the sleeping thread immediately, see the following case.

method illustrate
public void interrupt() Interrupt the thread associated with the object, if the thread is blocked, it will be notified in an abnormal way, otherwise the flag bit will be set
public static boolean interrupted() Determine whether the interrupt flag bit of the current thread is set, and clear the flag bit after calling
public boolean isInterrupted() Determine whether the flag bit of the thread associated with the object is set, and the flag bit is not cleared after calling

The details of the flags here are described later.

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(500);//随眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();//打印报错信息
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        Thread.sleep(3000); //睡眠 3 秒
        thread.interrupt();//中断 thread 线程,把标记位置为true。
        System.out.println("开始中断线程");
    }

Explain here! Thread.currentThread().isInterrupted(), Thread.currentThread() is to return the reference of the current thread object, which is similar to this:

method illustrate
public static Thread currentThread(); Returns a reference to the current thread object

isInterrupted(): Returns true if the current thread is interrupted, otherwise returns false.

The interrupt here should be viewed according to the interrupt flag bit. The interrupt flag bit (interrupt flag) is one of the internal states of the thread, and actually exists in the internal data structure of the thread. **Specifically, the interrupt flag bit is a member variable of the thread object, which is used to indicate the interrupt status of the thread. **This flag is initialized to false when the thread is created. When the interrupt() method of the thread is called, the interrupt flag will be set to true, indicating that the thread is interrupted.

Thread.currentThread().isInterrupted(), here indicates whether the current thread is interrupted, and returns true if interrupted, otherwise false. Finally, add a ! in front of it as the condition of while, that is, it will return false if it is interrupted, otherwise it will be true.

Finally, let's take a look at the results. Note that we have called thread.interrupt() above, so the expectation is that sleep throws an exception, and then ends the loop (end thread),

result:

Why doesn't the program end here? Let's analyze it slowly. It is obvious that the condition of the while loop is true if it is not over. Then the value of Thread.currentThread().isInterrupted() is false, which means that the thread is not in an interrupted state. What? Haven't we already called the interrupt() method?

The reason is: when the thread is blocked, such as calling the sleep (), join () or wait () method, if an interrupt request is received, these methods will throw InterruptedException and clear the interrupt status ! , the clear interrupt status here is to set the flag position to false, indicating that the thread has not been interrupted.

How to solve this problem? Just add break directly to the catch.

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(500);//随眠
                    } catch (InterruptedException e) {
                        //这里进行善后处理

                        break;
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        //中断线程
        Thread.sleep(3000); //睡眠 3 秒
        thread.interrupt();//中断 thread 线程
        System.out.println("开始中断线程");
    }

What are the advantages of doing something like this? The thread can handle the interrupt request according to its own logic, such as ending the loop, closing the resource or ignoring it.

Summary: preconditions, call interrupt();

  1. If the thread is blocked and suspended due to calling methods such as wait/join/sleep, it will be notified in the form of InterruptedException and the interrupt flag will be reset. Whether to end the thread at this time depends on the way the code in the catch is written. You can choose to ignore this exception, or you can jump out of the loop to end the thread.
  2. If there are no methods such as wait/join/sleep,
  3. Thread.interrupted() Determines whether the interrupt flag of the current thread is set, and resets the interrupt flag after judging.
  4. Thread.currentThread().isInterrupted() Determines whether the interrupted flag of the current thread is set, and does not reset the interrupted flag.

3.6 Waiting for thread join()

Sometimes, we need to wait for a thread to complete its work before proceeding to the next step.

method describe
join() Wait for the thread to complete
join(long millis) Wait up to the specified number of milliseconds, if the thread does not complete execution within this time, the current thread will continue to execute
join(long millis, int nanos) Wait up to the specified number of milliseconds and nanoseconds. If the thread does not complete execution within this time, the current thread will continue to execute
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 9; i++) {
                    System.out.println("thread");
                }
            }
        };

        thread.start();//开启线程

        System.out.println("join()前");
        try {
            thread.join();//先等 thread 线程执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join()后");
    }
}

If the thread thread has finished executing before join(), join() will not block.

These join() methods provide a cooperative execution mechanism between threads, allowing one thread to wait for another thread to complete before continuing to execute. This is useful for scenarios that require thread execution order or dependencies between threads.

Guess you like

Origin blog.csdn.net/mxt51220/article/details/131361893