一、什么是线程池(What)?

  • 首先,我们要理解什么是多线程。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
<多线程> 翻译成人话:类似大家进超市门,很多人挤一个入口通道,太慢了。那就多开通几个入口通道,短时间内解决更多人流量。
  • 然后,再来理解什么是线程池。可以理解成,事先创建好一堆线程,供多任务来使用这些线程,用完后归还到池内,在设置的某时间段内不销毁,以备其他任务使用。

二、为什么要使用线程池(Why)?

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间的。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?这就是线程池的诞生原因。

三、怎么使用线程池(How)?

多线程的使用很重要,java内置包里肯定就有解决方案。

线程池执行器:

java.util.concurrent.ThreadPoolExecutor

这里我们对这个执行器进行简单的设置,写成工具类,便于多处调用。代码如下:

1. 线程池执行器-工具类

import java.util.concurrent.*;

/**
 * @author dayaocool
 * @version 1.0
 * @date 2020/08/12 14:27
 */
public class ThreadPoolUtil {
    // 定义一些配置常量,也可以读取外部配置文件

    // 开启的核心线程数,(可更改查看不同)
    private static final int corePoolSize = 2;
    
    // 最大线程数量,不能比 开启的线程数(corePoolSize)小。
    private static final int maximumPoolSize = 8;
    

    /**
     * 线程执行器添加的任务,会先放到初始化 核心线程数处理(corePoolSize)。
     * 若 任务数 > 核心线程数,则把多余的任务 放到 该任务队列中(workQueue)等待。
     * 若 任务队列(workQueue) 仍然 不够 放置多余的任务,则会 对 核心线程数(corePoolSize)进行扩容,
     * 核心线程数(corePoolSize)扩容的最大极限 就是 最大线程数量(maximumPoolSize),
     * 若 核心线程数扩容到最大极限 和 队列数量 的总和 都 不够 放置 任务数, 则进行拒绝策略(MyRejectHandler),拒绝多余的任务。
     */
    private static final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(8);
    
    // 线程存活时间 (超过该时间无任何使用才回销毁线程)
    private static final long keepAliveTime = 2;
    
    // 线程存活时间的单位,秒
    private static final TimeUnit unit = TimeUnit.SECONDS;
    // 线程池工厂的 实现类
    
    private static final ThreadFactory threadFactory = new MyThreadPoolFactory();
    
    // 任务队列满载拒绝处理程序 实现类
    private static final RejectedExecutionHandler rejectedExecutionHandler = new MyRejectHandler();

    /**
     * 线程池工厂的 实现类 (内部类)
     */
    private static class MyThreadPoolFactory implements ThreadFactory {

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "新线程-");
            System.out.println(thread.getName() + "创建完成");
            return thread;
        }
    }

    /**
     * 任务队列满载拒绝处理程序 实现类 (内部类)
     */
    private static class MyRejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

            // ...拒绝处理的代码可扩展...

            System.out.println(r.toString() + " 已被拒绝");
        }
    }

    /**
     * 获取 线程池执行器
     * @return 线程池执行器
     */
    public static ThreadPoolExecutor getExecutor() {
        return new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                rejectedExecutionHandler
        );
    }
}

2. 模拟线程任务类

/**
 * @author dayaocool
 * @version 1.0
 * @date 2020/8/12 14:33
 */
public class MyTask implements Runnable {
    private String name;

    public MyTask(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟程序3秒耗时
            Thread.sleep(3000);

            System.out.println(this.toString() + " 运行结束了!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "大尧啊,您的 MyTask{" +
                "name='" + name + '\'' +
                '}';
    }
}

3. 运行调用

/**
 * @author dayaocool
 * @version 1.0
 * @date 2020/8/12 14:48
 */
public class RunTest {
    public static void main(String[] args) {
        // 模拟 5个 任务
        MyTask myTask1 = new MyTask("任务1");
        MyTask myTask2 = new MyTask("任务2");
        MyTask myTask3 = new MyTask("任务3");
        MyTask myTask4 = new MyTask("任务4");
        MyTask myTask5 = new MyTask("任务5");

        // 从线程池工具类 获取到 线程执行器
        ThreadPoolExecutor executor = ThreadPoolUtil.getExecutor();

        // 并发多线程 执行任务
        executor.execute(myTask1);
        executor.execute(myTask2);
        executor.execute(myTask3);
        executor.execute(myTask4);
        executor.execute(myTask5);
    }
}

输出

  • 先输出 初始化开启的线程数,corePoolSize=2,即开了2个线程
新线程-创建完成
新线程-创建完成
  • 继续输出 (任务先后问题这是cpu资源抢占时机的问题)
大尧啊,您的 MyTask{name='任务2'} 运行结束了!
大尧啊,您的 MyTask{name='任务1'} 运行结束了!
  • 继续输出
大尧啊,您的 MyTask{name='任务3'} 运行结束了!
大尧啊,您的 MyTask{name='任务4'} 运行结束了!
  • 最后输出
大尧啊,您的 MyTask{name='任务5'} 运行结束了!

5个任务,分2个并发,执行完成。

总结

线程池自动管理多线程的资源开销,让多线程编程更高效,解决多线程的线程开启关闭耗时的问题。spring boot 2框架对线程池的使用更简单,推荐。

转载 请注明出处 思否-是大尧啊 https://segmentfault.com/u/dayaocool



是大尧啊
6 声望0 粉丝

谋其上,而得其中;谋其中,而得其下。