Detailed explanation of thread pool and solving hair problems

This article has been included in the column
《Java》

concept note

What is a thread pool

  A thread pool is a mechanism for managing and reusing threads. It consists of a thread queue and a set of methods for managing threads. Threads in the thread pool can be reused to execute submitted tasks without creating and destroying threads each time.

Thread Pool Components

  Thread queue: The thread pool maintains a thread queue for storing tasks to be executed. When a task is submitted to the thread pool, the thread pool will take an idle thread from the queue to execute the task.
  Thread manager: The thread manager of the thread pool is responsible for creating, starting and stopping threads. It dynamically creates threads as needed and adds them to the thread queue. When a thread is no longer needed by the thread pool, the thread manager stops the thread and removes it from the thread queue.
  Task interface: The task interface in the thread pool defines the type of task to be executed. A task can be a normal task that implements the Runnable interface, or a task with a return value that implements the Callable interface.
  Task queue: The task queue of the thread pool is used to store tasks to be executed. When a task is submitted to the thread pool, the thread pool will add the task to the task queue and wait for the thread to execute.
insert image description here

Advantages and disadvantages

Advantages of thread pool

  • Reduce resource consumption: The thread pool can reuse created threads, avoiding the overhead of frequent creation and destruction of threads. The creation and destruction of threads need to consume system resources, including CPU and memory. By using the thread pool, the creation and destruction of threads can be concentrated in one place, reducing resource consumption and improving system performance and efficiency.
  • Improve response speed: The thread pool can improve the response speed of tasks. The threads in the thread pool are pre-created. When a task arrives, an idle thread can be allocated immediately to execute the task without waiting for the thread to be created and started. This can reduce the waiting time of the task and improve the response speed of the task.
  • Control the number of concurrent threads: The thread pool can control the number of concurrent threads, avoiding resource competition and performance degradation caused by too many threads in the system. By setting the size of the thread pool, the number of concurrently executing threads can be limited to avoid system overload. At the same time, the thread pool can also dynamically adjust the number of threads according to the load of the system to maintain the stability and performance of the system.
  • Provide thread management and monitoring functions: The thread pool provides some methods for managing and monitoring threads, which can better control and optimize the execution of threads. For example, attributes such as thread priority, timeout, and thread name can be set. At the same time, the thread pool also provides some monitoring methods, which can obtain information such as the status and execution status of the threads in the thread pool, so as to facilitate thread debugging and optimization.

Disadvantages of thread pool

  • Resource occupation: The thread pool will pre-create a certain number of threads, and these threads will always exist, even if there are no tasks to execute. This will occupy certain system resources, such as memory and CPU resources.
    Example: Suppose a thread pool is configured with 10 threads, but only 2 tasks need to be executed within a certain period of time, and the other 8 threads will always be idle and occupy resources.
  • Thread leak: Threads in the thread pool can be reused. When a task is executed, the thread will return to the thread pool and be marked as available. However, if an exception occurs during task execution and the thread is not returned to the thread pool correctly, it will result in a thread leak.
    Example: An exception occurred during the execution of a task, and the thread was not returned to the thread pool. In this way, the thread cannot be reused and will always occupy system resources.
  • Blocking problem: The threads in the thread pool are limited. When there are too many tasks, the thread pool may not be able to process tasks in time due to insufficient threads, resulting in tasks being blocked.
    Example: There are only 5 threads in the thread pool, but 10 tasks need to be executed at the same time. The first 5 tasks can be executed immediately, but the next 5 tasks need to wait for the execution of the previous tasks to be completed before they can be executed, causing the tasks to be blocked.

application

Create a thread pool

6 ways to create a thread pool
insert image description here

Create thread pool parameters
insert image description here

submit task

Two ways to submit tasks

1.executor: Submitting a Runnable task is to submit a task with no return value to the thread pool

Specific business class

public class TaskToRunnable  implements Runnable{
    
    

    @Override
    public void run() {
    
    
        //打印线程的名字
        System.out.println("线程名:"+Thread.currentThread().getName());
    }
}

client

public class Client {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建任务无返回值
        TaskToRunnable taskToRunnable=new TaskToRunnable();
        //创建单个线程的线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交任务
        threadPool.execute(taskToRunnable);
        //关闭线程池
        threadPool.shutdown();
    }
}

operation result
insert image description here

2.submit: Submit a Callable task, which is to submit a task with a return value to the thread pool

Specific business class

public class TaskToCallable implements Callable<Integer> {
    
    
    @Override
    public Integer call() {
    
    
        return 5+5;
    }
}

client

public class Client {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        //创建任务有返回值
        TaskToCallable taskToCallable=new TaskToCallable();
        //创建单个线程的线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交任务
        Future<Integer> submit = threadPool.submit(taskToCallable);
        try{
    
    
            System.out.println("返回值为:"+submit.get());
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            //关闭线程池
            threadPool.shutdown();
        }
    }
}

operation result
insert image description here

cancel task

Canceling tasks is divided into three phases: canceling unexecuted tasks, canceling running tasks, and canceling completed tasks. The following will show you their effects in the form of code

Cancel unexecuted tasks

client code

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消未执行的任务
 * @CreateTime: 2023-07-23 23:42
 * @Version: 1.0
 */

public class UnExecutedClient {
    
    
    public static void main(String[] args) {
    
    
        TaskToCallable taskToCallable=new TaskToCallable();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future1 = threadExecutor.submit(taskToCallable);
        Future<Integer> future2 = threadExecutor.submit(taskToCallable);
        boolean cancel = future2.cancel(false);
        System.out.println("任务2是否取消成功"+cancel);
        try{
    
    
            Integer integer = future2.get();
            System.out.println("任务2的返回值"+integer);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

The three examples of the business code are the same. The following two examples are not written anymore.

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.commit
 * @Author: Wuzilong
 * @Description: 业务代码
 * @CreateTime: 2023-07-23 22:58
 * @Version: 1.0
 */

public class ExecuteClientTask implements Callable<Integer> {
    
    
    @Override
    public Integer call() throws Exception {
    
    
        int i=0;
        //线程没有被中断递增i
        while(!Thread.interrupted()){
    
    
            i++;
        }
        System.out.println("当前i的值为:"+i);
        return 5+5;
    }
}

operation result
insert image description here

  We created a thread pool with a single thread and created two threads. Such a thread is in the thread queue and a thread is in the task queue. We cancel the thread in the task queue and get the value of the canceled thread. You will find that an error will be reported when getting the value, indicating that the thread has been cancelled.

Cancel an ongoing task

client code


/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消执行中的任务
 * @CreateTime: 2023-07-23 23:50
 * @Version: 1.0
 */

public class ExecuteClient {
    
    
    public static void main(String[] args) {
    
    
        ExecuteClientTask taskToCallable=new ExecuteClientTask();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future = threadExecutor.submit(taskToCallable);
        System.out.println("任务是否完成:"+future.isDone());
        //参数为false执行结果显示取消成功但是程序没有结束    任务继续执行完
        //参数为true直接结果显示取消成功程序结束了
        boolean cancel = future.cancel(true);
        System.out.println("任务1是否取消成功"+cancel);
        try{
    
    
            Integer integer = future.get();
            System.out.println("获取任务1的结果:"+integer);

        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

As a result of the operation,
insert image description here
  we also created a thread pool that serves as a thread. After the task is submitted, the cancel operation is performed directly. When performing the cancel operation, we will pass in a parameter, true or false: the parameter is false, and the execution result shows that the cancellation is successful but the program If it is not over, the task continues to execute. The parameter is true and the direct result shows that the cancellation is successful and the program is over. In the above example, true is passed in, and the value cannot be obtained below, and the error of thread cancellation will be reported directly.

Cancel a completed task

client code

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消已完成的任务  无法取消
 * @CreateTime: 2023-07-23 23:54
 * @Version: 1.0
 */

public class ExecutedClient {
    
    
    public static void main(String[] args) {
    
    
        TaskToCallable taskToCallable=new TaskToCallable();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future = threadExecutor.submit(taskToCallable);

        try{
    
    
            Integer integer = future.get();
            System.out.println("获取任务1的结果:"+integer);
            //参数只对运行时的任务有效
            boolean cancel = future.cancel(false);
            System.out.println("任务1是否取消成功"+cancel);
            System.out.println("任务1的返回值"+integer);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

As a result of the operation,
insert image description here
  we also created a thread pool as a thread. After submitting the task, we obtained the return result of the task, and then canceled the task. It is found that the return value of the task can still be obtained after cancellation, so the task that has been executed cannot be canceled.

insert image description here

reject task

Business logic

public class Task implements Runnable{
    
    
    private final int index;

    public Task(int index){
    
    
        this.index=index;
    }

    @Override
    public void run() {
    
    
        System.out.println("输出线程名:"+Thread.currentThread().getName()+"线程编号:"+index);
    }
}

client

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.refuse
 * @Author: Wuzilong
 * @Description: 线程池拒绝任务
 * @CreateTime: 2023-07-24 01:08
 * @Version: 1.0
 */

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //直接丢弃,只有前两个线程
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.DiscardPolicy());
        //拒绝的任务交个调用者的线程去执行
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.CallerRunsPolicy());
        //默认的拒绝策略,直接抛出异常信息
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
        //丢弃队列头部的任务,添加新的任务
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),new ThreadPoolExecutor.DiscardOldestPolicy());

        try{
    
    
            threadPoolExecutor.execute(new Task(1));
            threadPoolExecutor.execute(new Task(2));
            threadPoolExecutor.execute(new Task(3));
            //丢弃头部的任务需要打开,前三个注释掉即可
            threadPoolExecutor.execute(new Task(4));

        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            //关闭线程
            threadPoolExecutor.shutdown();
        }
    }
}

Running results
insert image description here
  We are now using the DiscardOldestPolicy rejection strategy to create a thread pool with one thread queue and two task queues. We added four threads to the thread pool. The result of execution is that the second thread is not executed, and the fourth thread is executed. This is consistent with the DiscardOldestPolicy rejection policy we set, canceling the head thread in the task queue and adding a new thread to the task queue.
insert image description here

close operation

  After closing the thread pool, the thread pool will not continue to receive new tasks, and will continue to execute the tasks in the task queue

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.close
 * @Author: Wuzilong
 * @Description: 关闭线程池,不继续接收新的任务,会继续执行完任务队列中的任务
 * @CreateTime: 2023-07-24 08:54
 * @Version: 1.0
 */

public class Client {
    
    
    public static void main(String[] args) {
    
    
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
        try{
    
    
            threadPoolExecutor.execute(new Task(1));
            //将任务队列中的任务继续执行完毕
            threadPoolExecutor.execute(new Task(3));
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadPoolExecutor.shutdown();
            //关闭线程之后继续提交任务看看是否被拒绝
            //threadPoolExecutor.execute(new Task(2));
        }
    }
}

Running result
1. The result of continuing to execute the task
insert image description here
2. The result of rejecting the task
insert image description here

delayed operation

perform a task

client code

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.schedule(task,3, TimeUnit.SECONDS);
        scheduledExecutorService.shutdown();
    }
}

specific business code

public class Task implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("执行方法时间"+ LocalTime.now());
    }
}

Running Results
insert image description here
Submitting a Runnable task is the same as submitting a Callable task. Just adjust the implemented interface. There are not too many demonstrations here. The parameters in the schedule method: the first is the task object, the second is the time, and the third is the time unit.

repetitive tasks

Fixed time

specific business code

public class Task implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("执行方法时间"+ LocalTime.now());
        try{
    
    
            //线程休眠1秒钟,模拟任务执行时长
            Thread.sleep(1000);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

client code

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.scheduleAtFixedRate(task,1,1, TimeUnit.SECONDS);
        try{
    
    
            //当前下次线程休眠5秒
            TimeUnit.SECONDS.sleep(5);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}

The running result
insert image description here
is executed every second. The parameters passed in the scheduleAtFixedRate method are the tasks to be executed, the delay time and the fixed time for each delay. In the example, 1 is passed in to indicate that it is executed every second. The most effective One is the unit of time.

Intervals

client code

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.scheduleWithFixedDelay(task,2,1, TimeUnit.SECONDS);
        try{
    
    
            //当前下次线程休眠5秒
            TimeUnit.SECONDS.sleep(5);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}

The specific business code is consistent with the fixed time and will not be repeated here.
operation result
insert image description here

Execute once every two seconds on the premise that the previous task is executed. The parameters passed in the scheduleWithFixedDelay method are the task to be executed, the delay time and the fixed time for each delay. In the example, 2 is passed in to indicate that it is before On the premise that a task is executed, it is executed every two seconds, and the last one is the unit of time.

insert image description here

Comparison of fixed time and interval time

insert image description here

Guess you like

Origin blog.csdn.net/weixin_45490198/article/details/131863455