Programming Essentials: Detailed Explanation of JAVA Multithreading

Table of contents

foreword

1. Getting started with multithreading

1.1. Thread, process, multi-thread, thread pool

1.2. Concurrent, Serial, Parallel

1.3. Implementation of threads

1.3.1. Inherit the Thread class

1.3.2. Implement the Runnable interface

1.3.3. Using Callables and Futures

1.3.4. Using the thread pool

1.4. The state of the thread

1.5. Thread common methods

1.5.1 sleep()

1.4.2 join()

1.5.3 yield()

1.5.4. wait() and notify() 

1.5.5.-interrupt() method and stop() method 

1.5.6. setPriority (int newPriority)、getPriority()

1.6. Thread scheduling

2. Advanced multithreading

2.1. Problems caused by multithreading

2.2. Multithreading solution

2.2.1. Lock mechanism

2.2.3. Thread pool

2.2.4. Concurrency Tools

3. Interview questions

3.1. What is the difference between sleep() and wait()?

3.2. How to stop a running thread

3.3. The difference between interrupt(), interrupted(), and isInterrupted() methods

3.4. What is the impact of too many threads created by the system?

4. Write at the end

thank you


foreword

Multithreading has become a common programming pattern, widely used in many different types of applications.

In this blog post, we will explore the relevant knowledge and skills of multithreaded programming. Through code examples and practical application cases, we can gain an in-depth understanding of the specific implementation and application methods of multithreading, help to better master multithreading programming technology, and improve program efficiency and performance. Later, as the study progresses, it will be supplemented and revised.

1. Getting started with multithreading

1.1. Thread, process, multi-thread, thread pool

thread

In computer science, a thread refers to a single path of execution within a process. A process can contain multiple threads, and each thread can perform different tasks in parallel. Multithreaded programming refers to running multiple threads at the same time to complete multiple tasks.

Multithreading

Multithreading refers to running multiple threads at the same time to complete multiple tasks. Multithreading improves program performance and responsiveness. However, the complexity of the code is increased, and issues such as thread safety and deadlock need to be considered.

Thread Pool

A thread pool is a set of pre-created threads that can be reused to perform multiple tasks. Using the thread pool can avoid extra overhead when creating and destroying threads, thereby improving the performance of the program. Executor framework is provided in Java to implement thread pool.

process

A process is the execution process of a program, which is a running activity of a program in a computer on a certain data set, and is the smallest unit of resource allocation by the system

The difference between thread and process

  • The fundamental difference: a process is the smallest unit of operating system resource allocation, while a thread is the smallest unit that handles task scheduling and execution
  • Resource overhead: Each process has its own code and data space, and switching between processes will have a large overhead ; threads can be run as lightweight processes, and threads of the same type share code and data spaces, and each thread has It has its own independent running stack and program counter, and the switching overhead between threads is small
  • Containment relationship: a process can contain multiple threads, and these threads can share synchronization resources
  • Memory allocation: All threads in the same process share the address space and resources of the process, while the address spaces and resources between processes are independent of each other
  • Impact relationship: After a process crashes, it will not affect other processes; and after a thread crashes, other threads will also be affected, so the entire process crashes
  • Execution process: Each independent process has a program running entry, sequential execution sequence and exit, but threads cannot be executed independently and must depend on the process

1.2. Concurrent, Serial, Parallel

Concurrency, serial, parallel concepts

  • Concurrency: Refers to the alternate execution of multiple tasks on the same CPU in the same period of time, which seems to be executed at the same time. For example, multiple threads running at the same time.
  • Serial: Refers to the execution of multiple tasks in sequence, and the next task can only be executed after one task is completed. For example, a single-threaded program is executed serially.
  • Parallelism: Multiple processors or multi-core processors can process multiple tasks at the same time, which must be achieved with multiple processors or multi-core CPUs, otherwise it can only be concurrent. For example, multiple threads running on different processors or CPUs.

The difference between concurrent, serial, and parallel

  • Execution mode: Both concurrent and serial are executed on a single processor, but concurrency is the alternate execution of multiple tasks, and serial is sequential execution; parallelism requires multiple processors or multi-core CPUs to achieve.
  • Performance: Both concurrency and parallelism can improve program performance and response speed, but concurrency needs to consider issues such as thread safety and deadlocks; although serial is simple and stable, it cannot make full use of the advantages of multi-core CPUs.
  • Implementation method: Concurrency can be achieved using multi-threading technology; serial can only be achieved using single thread; parallelism requires multiple processors or multi-core CPUs to achieve.

Three Elements of Concurrent Programming

  • Atomicity: means that one or more operations either all succeed or all fail
  • Visibility: when one thread modifies a shared variable, another thread can see it immediately
  • Orderedness : The order in which the program is executed is executed in the order of the code (the processor may reorder the instructions)

Multithreading and Concurrency

Multithreading refers to running multiple threads at the same time to complete multiple tasks. Multithreading can improve a program's performance and responsiveness because they can perform multiple tasks simultaneously.

Concurrency refers to the ability to execute multiple tasks at the same time. Concurrency can be achieved by using multithreading, but it can also be achieved in other ways, such as using asynchronous programming or event-driven programming.

Therefore, multithreading is a way to achieve concurrency, but concurrency does not necessarily require the use of multithreading. In addition, issues that need to be considered in multi-threaded programming, such as thread safety and deadlock, also need to be considered in concurrent programming

1.3. Implementation of threads

        Multithreading is an important means to improve program performance and response speed. There are many ways to implement it in Java. 

  • Inherit the Thread class
  • Implement the Runnable interface
  • Using Callable and Future
  • use thread pool

1.3.1. Inherit the Thread class

  • Introduction: Multi-threading is achieved by inheriting the Thread class.
  • Sample code: Shows how to inherit Thread class and override run() method.
  • Advantages: Simple to implement and easy to understand.
  • Disadvantage: Cannot inherit from other classes because Java does not support multiple inheritance.
class MyThread extends Thread {
    public void run() {
        // 执行需要的代码
    }
}

MyThread thread = new MyThread();
thread.start();

1.3.2. Implement the Runnable interface

  • Introduction: Multi-threading is realized by implementing the Runnable interface.
  • Sample Code: Shows how to implement the Runnable interface and override the run() method.
  • Pros: Can inherit from other classes because Java supports implementing multiple interfaces.
  • Disadvantages: Thread objects need to be created to start threads.
  • Steps:
     - Customize the thread class to implement the Runnable interface
     - Implement the run() method, write the thread body
     - Create a thread object, call the start() method to start the thread (it may not be executed immediately after startup, it can only be executed when CPU resources are grabbed)
class MyRunnable implements Runnable {
    public void run() {
        // 执行需要的代码
    }
}

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

1.3.3. Using Callables and Futures

  • Introduction: Multi-threading is achieved by using the Callable and Future interfaces.
  • Sample code: Shows how to use Callable and Future interfaces to create and start threads.
  • Advantages: You can get the return value of thread execution.
  • Disadvantages: Compared with the first two methods, the implementation is slightly more complicated.
  •  Steps:
     - Implement the Callable interface, first return the value type
     - Rewrite the call() method, you need to throw an exception
     - Create the target object
     - Create the execution service: ExecutorService ser = Executor.newFixedThreadPool(1);
     - Submit for execution: Future<Boolean > res = ser.submit(t1);
     - Get the result: boolean r1 = res.get();
     - Shut down the service: ser.shutdownNow();
import java.util.concurrent.*;

// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        // 线程执行体
        for (int i = 0; i < 10; i++) {
            System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
        }

        return true;
    }

    public static void main(String[] args) throws ExecutionException,
        InterruptedException {
        // main线程,主线程

        // 创建线程实现类对象
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        // 创建执行服务,参数是线程池线程数量
        ExecutorService ser = Executors.newFixedThreadPool(2);
        // 提交执行
        Future<Boolean> res = ser.submit(thread);
        Future<Boolean> res2 = ser.submit(thread2);
        // 获取结果
        boolean r1 = res.get();
        boolean r2 = res2.get();
        // 关闭服务
        ser.shutdownNow();
    }
}

1.3.4. Using the thread pool

  • Introduction: Multi-threading is achieved by using a thread pool.
  • Sample code: Show how to use Executor and ExecutorService interface to create and manage thread pool.
  • Advantages: threads can be reused, avoiding the overhead of frequently creating and destroying threads.
  • Disadvantages: The size of the thread pool needs to be properly configured, otherwise it may cause performance problems.
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() {
    public void run() {
        // 执行需要的代码
    }
});

1.4. The state of the thread

  • New (New): When the thread object is created, the thread is in the new state.
  • Run (Runnable): When the start() method of the thread is called, the thread is in the ready state, waiting for the CPU to schedule execution.
  • Blocked (Blocked): When a thread waits for a condition (such as I/O operations, locks), the thread is blocked.
  • Waiting (Waiting): When a thread waits for a certain condition to wake up (such as calling the wait() method), the thread is in a waiting state.
  • Timed Waiting (Timed Waiting): When a thread waits for a certain condition to wake up, but will automatically wake up after waiting for a certain period of time (such as calling the sleep() method or the wait() method with a timeout parameter), the thread is in the timed waiting state.
  • Terminated: When the thread execution is completed or an exception occurs, the thread is in the terminated state.

1.5. Thread common methods

  • start(): Start the thread.
  • run(): The code executed by the thread.
  • join(): Wait for the thread to finish executing.
  • sleep(): Make the thread sleep for a period of time.
  • interrupt(): Interrupts the execution of the thread.
  • wait() causes the current thread to wait for a notification from another thread.
  • notify() Notifies the waiting thread to continue execution.
  • setPriority (int newPriority), getPriority() Change and get the priority of the thread.

1.5.1 sleep()

The sleep() method can make the current thread pause for the specified time. For example:

try {
    Thread.sleep(1000); // 暂停 1 秒钟
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.4.2 join()

The join() method can wait for the specified thread to finish executing. For example:

Thread thread1 = new Thread(() -> {
    // 执行需要的代码
});

Thread thread2 = new Thread(() -> {
    // 执行需要的代码
});

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

try {
    thread1.join(); // 等待 thread1 执行完毕
    thread2.join(); // 等待 thread2 执行完毕
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.5.3 yield()

Suspend the currently executing thread object and execute other threads. The yield() method can give up CPU time slices so that other threads have a chance to run.

For example:

Thread.yield();

The execution of the yield() method will return the current thread from the "running state" to the "ready state".

1.5.4. wait() and notify() 

In a multithreaded program, you can use wait()the and notify()methods to coordinate operations among multiple threads. wait()method can make the current thread wait for another thread to issue a notification, and notify()the method can notify the waiting thread to continue execution.

public class Message {
    private String content;
    private boolean available = false;

    public synchronized String getContent() {
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        available = false;
        notifyAll();
        return content;
    }

    public synchronized void setContent(String content) {
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.content = content;
        available = true;
        notifyAll();
    }
}

public class Main {
    public static void main(String[] args) {
        Message message = new Message();
        // 创建两个线程来发送和接收消息
        new Thread(() -> {
            message.setContent("Hello");
        }).start();
        new Thread(() -> {
            System.out.println(message.getContent()); // 输出 Hello
        }).start();
    }
}

1.5.5.-interrupt() method and stop() method 

- The stop method provided by JDK is obsolete and not recommended.

- It is recommended that the thread stop automatically. It is recommended to use a flag variable to terminate. When flag=false, the thread will be terminated.

public class DemoInterrupt {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
        t.interrupt();
        //        t.stop(); //强行终止线程
        //缺点:容易损坏数据  线程没有保存的数据容易丢失
    }
}
class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            // 睡眠1天
            Thread.sleep(1000 * 60 * 60 * 24);
        } catch (InterruptedException e) {
        //            e.printStackTrace();
        }
        //1天之后才会执行这里
        System.out.println(Thread.currentThread().getName() + "---> end");
 
    }
}

1.5.6. setPriority (int newPriority)、getPriority()

Java provides a thread scheduler to monitor all threads that enter the ready state after startup in the program. The thread scheduler decides which thread should be scheduled for execution according to the priority. - The priority of the thread is represented by data, ranging from 1 to 10. - The high priority of a thread just means that its weight is high, and the probability of obtaining CPU execution power is high. - Set the priority of the thread first, then execute the start() method

public class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程优先级:"
            + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"a");
        Thread thread2 = new Thread(myThread,"b");
        Thread thread3= new Thread(myThread,"c");
        Thread thread4= new Thread(myThread,"d");
        thread3.setPriority(Thread.MAX_PRIORITY);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.NORM_PRIORITY);
        thread4.setPriority(8);
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

The result is as follows:

c线程优先级:10
b线程优先级:5
a线程优先级:1
d线程优先级:8

1.6. Thread scheduling

  • thread scheduling model

    • Evenly distributed scheduling model: All threads use the CPU usage rights in turn, and allocate CPU time equally to each thread.

    • Preemptive scheduling model: Let the threads with high priority use the CPU first. If the threads have the same priority, then a thread will be randomly selected for execution. The high-priority CPU will take up a little more time.

    The JVM in Java uses a preemptive scheduling model

  • getPriority(): get thread priority

  • setPriority: set thread priority

2. Advanced multithreading

2.1. Problems caused by multithreading

Multi-threaded programming can improve the performance and responsiveness of the program, but it can also bring some problems. It mainly includes the following aspects:

  1. Race Condition (Race Condition): When multiple threads access shared resources at the same time, due to the uncertainty of the thread execution order, it may cause errors in the output of the program. For example, if multiple threads increment a counter at the same time, if they are not synchronized, the value of the counter may be incorrect.

        You can use the synchronized keyword to resolve race conditions.

  2. Deadlock (Deadlock): When multiple threads wait for each other to release the resources occupied by each other, they may fall into a deadlock state and cannot continue to execute. For example, thread A occupies resource 1 and waits for resource 2, while thread B occupies resource 2 and waits for resource 1, neither thread can proceed.

        Deadlocks can be resolved using locks and condition variables.

  3. Starvation: When some threads cannot continue to execute due to failure to compete for shared resources, starvation may occur. For example, if a thread requests a resource in a heavily loaded system, it may wait a long time for the resource it needs.

       4. Context switching : When multiple threads are running at the same time, the operating system needs to perform context switching, which consumes certain system resources. If the number of threads is too large, the overhead of context switching may exceed the overhead of the program itself

        5. Thread safety: In multi-threaded programs, it is necessary to ensure the safety of shared resources. Thread safety can be achieved using locks and atomic variables.

        6. Performance optimization: In multi-threaded programs, performance optimization issues need to be considered. You can use thread pools and concurrent collections to improve program performance.

2.2. Multithreading solution

2.2.1. Lock mechanism

  • Definition: The lock mechanism is a way to solve mutually exclusive access to shared resources between multiple threads.
  • Implementation method: use the synchronized keyword, the ReentrantLock class or the ReadWriteLock interface to implement the lock mechanism.
  • Commonly used tools: Lock, Condition, Semaphore, ReadWriteLock, etc.

2.2.3. Thread pool

  • Definition: The thread pool is a mechanism for managing and scheduling multiple threads, which can avoid the performance overhead caused by frequent creation and destruction of threads.
  • Implementation method: use the Executors class or ThreadPoolExecutor class to create and manage thread pools.
  • Common parameters: number of core threads, maximum number of threads, task queue, rejection policy, etc.

2.2.4. Concurrency Tools

  • Definition: The concurrent tool class is a tool to solve common problems in concurrent programming, such as blocking queues, counters, semaphores, etc.
  • Implementation method: Use the tool classes provided in the java.util.concurrent package to implement concurrent programming.
  • Common tools: ArrayBlockingQueue, CyclicBarrier, Semaphore, CountDownLatch, etc.

3. Interview questions

3.1. What is the difference between sleep() and wait()?

Both suspend the current thread

  • The classes are different: sleep() is a static method of the Thread class, and wait() is a method of the Object class
  • Release the lock: sleep() will not release the lock, wait() will release the lock
  • Different purposes: wait() is usually used for inter-thread communication, sleep() is usually used to suspend threads
  • The results are different: after the sleep() method is executed, the thread will enter the ready state again; after the wait() method is woken up by notify(), the thread will enter the synchronization queue to re-preempt the lock

3.2. How to stop a running thread

  1. The thread is automatically terminated after the execution of the thread's run() method ends
  2. Use the stop() method to force termination, but it is rarely used in this way
  3. Use the interrupt() method to interrupt the thread (the process is to set an interrupt flag bit, call the interrupt() method to notify the system to request to close the thread, and wait for the system to interrupt itself at an appropriate time)

3.3. The difference between interrupt(), interrupted(), and isInterrupted() methods

  • The interrupt() method and isInterrupted() method are both instance methods, called by the Thread object; interrupted() is a static method, called by the Thread class, and the three methods are related to thread interruption
  • interrupt() method: used to set the interrupt flag bit, notify the system to request to end the thread, and the system decides when to interrupt

At this time, if the thread is blocked:
then an InterruptedException will be thrown and the interrupt flag will be reset

If the thread is not blocked:
Use Thread.interrupted() to determine whether the current interrupt flag is set, and reset the interrupt flag
Use Thread.currentThread.isInterrupted() to determine whether the current interrupt flag is set, and do not reset the interrupt flag

3.4. What is the impact of too many threads created by the system?

  • Thread lifetime overhead is very high
  • consumes too much CPU
  • Reduce the efficiency of the JVM

4. Write at the end

The above is my personal introduction to multithreading, and I will continue to improve and update it in the future to encourage everyone

thank you

[1] https://blog.csdn.net/zdl66/article/details/126297036

[2] https://blog.csdn.net/m0_46233999/article/details/118702235

[3] https://blog.csdn.net/qq_29141913/article/details/125964815#3_16

[4] https://blog.csdn.net/YQQAGH178/article/details/119828128

Guess you like

Origin blog.csdn.net/qq_20957669/article/details/131201017