Exploring Multithreaded Programming: The Nature, State, and Properties of Threads

Multithreading is an important and powerful concept in modern computer programming. It allows us to more efficiently utilize multi-core processors, improve program performance, and achieve concurrent operations.

what is thread

Thread is the smallest unit of program execution, and it is the basic unit of operating system scheduling . Unlike processes, threads share the same process memory space , which means they can access the same data and resources. Communication between threads is easier, but also requires more careful synchronization to avoid race conditions and data collisions.
Each task is executed in a thread, which is short for thread of control. A program that runs multiple threads at the same time is said to be multithreaded.
Let's write a multi-threaded program to see the execution results:

public class main {
    
    
    public static void main(String[] args) {
    
    
        //构造一个新线程
        Thread t1 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 200; i++) {
    
    
                    System.out.println("这是第一个线程!");
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 200; i++) {
    
    
                    System.out.println("这是第二个线程!");
                }
            }
        });
        System.out.println("线程1启动");
        //启动新线程
        t1.start();
        System.out.println("线程2启动");
        t2.start();
    }
}

insert image description here
Through the results we can see that the two threads are executed at the same time. Several ways to create threads can read the blog
written before .

thread state

Threads have the following 6 states:

  • New
  • Runnable
  • Blocked
  • Waiting
  • Time waiting
  • Terminated

new thread

When a new thread is created with the new operator, the thread has not yet started running.
For example:

        //构造一个新线程
        Thread t1 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 200; i++) {
    
    
                    System.out.println("这是第一个线程!");
                }
            }
        });

runnable thread

When the newly created thread calls the start method, it is in a runnable state. A runnable thread may or may not be running. This is because the preemptive scheduling system gives each runnable thread a time slice to execute tasks. Each processor runs a thread. When the number of threads is more than the number of processors, the scheduler needs to allocate time slices to allow multiple threads to run in parallel.

Blocking and waiting threads

When a thread is blocked or waiting, it is temporarily inactive.

  • The thread tries to acquire the internal object lock, and this lock is occupied by other threads, so that the thread is blocked.
  • Threads wait for notifications or interrupt signals from other threads. For example, calling Object.waita method, Thread.jointhe method puts the thread into a waiting state.
  • There is also the use Thread.sleep,, method to make the thread enter the timing waiting state Object.wait.Thread.join

For example: Thread.sleep(1000);make the thread wait for one second.

terminate thread

  • The run method exits normally, and the thread ends naturally
  • An uncaught exception terminated the run method, causing the thread to terminate unexpectedly

We can getState()get the status of the current thread by.

thread properties

The attributes of a thread refer to some characteristics and configuration options of the thread that can affect the behavior and performance of the thread.

priority

The priority is used to determine the priority of the thread when competing for CPU time, that is, it determines the probability of the thread being scheduled for execution. Each thread has a default priority, usually Normal, ranging from 1 (lowest priority) to 10 (highest priority). Thread priorities can be set and adjusted programmatically.

A thread's priority determines the order in which it competes with other threads for CPU time. The scheduling algorithm of the operating system usually considers the priority of the thread to decide which thread to allocate CPU time to. Compared with low-priority threads, high-priority threads have a higher probability of obtaining CPU time slices, that is, they have more opportunities to perform tasks. However, priority is not absolute, but a relative concept. High-priority threads are not necessarily executed before low-priority threads.

Here is a simple example to explain the priority:

public class test1 implements Runnable {
    
    
    public void run() {
    
    
        for (int i = 0; i < 5; i++) {
    
    
            System.out.println(Thread.currentThread().getName() + " is running");
        }
    }

    public static void main(String[] args) {
    
    
        Thread thread1 = new Thread(new test1());
        Thread thread2 = new Thread(new test1());

        thread1.setPriority(Thread.MIN_PRIORITY); // 设置线程1的优先级为最低
        thread2.setPriority(Thread.MAX_PRIORITY); // 设置线程2的优先级为最高

        thread1.start();
        thread2.start();
    }
}

insert image description here
In the above example, we created two threads thread1 and thread2 and set them as the lowest and highest priority respectively. In the run method, each thread prints its own name in a loop. Since thread 2 has a higher priority, it has a higher probability of CPU time slice allocation, so it is more likely to be scheduled first in the competition. However, this result is not absolute, and thread 1 still has the opportunity to execute before thread 2 in some cases.

thread name

The thread name is used to give a thread a recognizable and identifying name. Each thread can be named, which makes it easier to identify and track thread execution when debugging and logging. Thread names can help us distinguish different threads and understand their role in the program.
In Java, you can setNameset a name for a thread through a method, and you can also getNameget the name of a thread through a method. The thread name is usually a descriptive string, which can be named according to the characteristics and context of the task, so as to better reflect the function of the thread.

daemon thread

Daemon Thread refers to a thread that executes silently in the background while the program is running, and it does not prevent the program from exiting. Daemon threads are automatically terminated when all non-daemon threads end . They are usually used to perform some background tasks or provide a service function without interfering with or blocking the normal execution of the main program.
The life cycle of the daemon thread ends with the life cycle of the program . Daemon threads are automatically stopped when all non-daemon threads are terminated, even if they are executing some tasks. In order to identify a thread as a daemon thread, you can use setDaemon(true)the method setting of the thread object, which must be called before the thread starts .

interrupt thread

Interrupting a thread is when, in multithreaded programming, a thread is asked to stop executing or is forcibly terminated. This is usually achieved by sending an interrupt signal or setting a flag, allowing the thread to safely stop execution and release resources after receiving the interrupt request. The purpose of interrupting a thread is usually to prevent the thread from exiting normally or to handle some abnormal conditions.
Here is a simple Java example that demonstrates how to interrupt a thread:


public class InterruptExample {
    
    

    public static void main(String[] args) {
    
    
        Thread myThread = new Thread(new MyRunnable());
        myThread.start(); // 启动线程

        // 主线程等待一段时间后中断myThread
        try {
    
    
            Thread.sleep(2000); // 等待2秒钟
            myThread.interrupt(); // 发送中断信号
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

    static class MyRunnable implements Runnable {
    
    
        @Override
        public void run() {
    
    
            while (!Thread.currentThread().isInterrupted()) {
    
    
                System.out.println("线程正在执行...");
                try {
    
    
                    Thread.sleep(1000); // 线程每隔1秒钟执行一次
                } catch (InterruptedException e) {
    
    
                    // 捕获到中断信号后,线程会退出循环
                    System.out.println("线程被中断,即将退出...");
                    Thread.currentThread().interrupt(); // 重新设置中断状态
                }
            }
        }
    }
}

insert image description here

In the above example, we created a thread ( myThread), and then waited for 2 seconds in the main thread before sending an interrupt signal to myThread. In MyRunnablea thread, the thread executes continuously but checks for an interrupt signal. If an interrupt signal is received, the thread will InterruptedExceptionexit the loop after capturing it, and complete the interrupt operation of the thread.
It should be noted that interrupting the thread does not force the thread to terminate, but by setting an interrupt flag to allow the thread to terminate itself at an appropriate time. This ensures that the necessary cleanup can be done when the thread terminates to avoid resource leaks and inconsistent state .

interrupted and isInterrupted, the interrupted method is a static method, which checks whether the current thread is interrupted, and clears the interrupt status of the thread, isInterrupted is an instance method, used to check whether a thread is interrupted.

Handler for uncaught exceptions

Uncaught Exception Handler (Uncaught Exception Handler) is a mechanism used in multi-threaded or multi-threaded applications to handle uncaught exceptions. When a thread throws an uncaught exception, if the thread does not explicitly catch the exception, the exception will be passed to the thread's uncaught exception handler, which can be customized to record exception information, Perform a specific action, or perform a safe exit of the application.
Here is a simple Java example that demonstrates how to set an uncaught exception handler:


public class UncaughtExceptionHandlerExample {
    
    

    public static void main(String[] args) {
    
    
        // 设置未捕获异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler());

        // 创建一个线程并启动它,该线程会抛出一个未捕获异常
        Thread thread = new Thread(() -> {
    
    
            throw new RuntimeException("这是一个未捕获的异常");
        });
        thread.start();
    }

    static class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
    
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
    
    
            System.err.println("线程 " + t.getName() + " 抛出了未捕获的异常: " + e.getMessage());
            // 在这里可以添加自定义处理逻辑,如记录日志、发送通知等
        }
    }
}

insert image description here

In the above example, we first Thread.setDefaultUncaughtExceptionHandlerset a custom uncaught exception handler via the method ( CustomExceptionHandler). Then, a thread is created and started, which intentionally throws an uncaught exception. When this exception is thrown, it will be passed to the uncaught exception handler we set, the method CustomExceptionHandlerin it will be executed, and the exception information will be printed. uncaughtExceptionYou can add any custom processing logic you need in this method, such as logging or sending notifications.

Guess you like

Origin blog.csdn.net/st200112266/article/details/132717488