线程池原理及使用

一、背景

线程池的作用是管理线程对象,避免重复new Thread消耗资源,控制资源的访问

二、原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFGuLGsE-1626412263276)(images/线程池.png)]

线程池由主线程创建,刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

调用 execute() 方法添加一个任务时,线程池会做如下判断:

  1. 如果正在运行的线程数量小于 corePoolSize,创建线程运行这个task,否则下一步;
  2. 如果正在运行的线程数量大于或等于 corePoolSize且阻塞队列没有满,把task放入队列,否则下一步。
  3. 如果在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行阻塞队列的task,否则下一步;
  4. 拒绝策略。

执行完成后

  1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  2. 当一个线程空闲时,超过一定的时间(keepAliveTime)时,且当前运行的线程数大于 corePoolSize,这个线程就被停掉。直到 当前运行的线程数等于corePoolSize 。

三、4种创建方法

  • 官方强烈建议使用更方便的 Executors工厂方法

创建方法官方解释参数
1Executors.newCachedThreadPool()无界线程池,具有自动线程回收核心线程数为0,最大线程数为Integer.MAX_VALUE
2Executors.newFixedThreadPool(int)固定大小的线程池工作队列无界
3Executors.newSingleThreadExecutor()单个后台线程只有一个核心线程
4new ThreadPoolExecutor()参考7大参数参考7大参数
  • 使用构造函数创建线程池

// 使用给定的初始参数创建一个新的ThreadPoolExecutor 。
java.util.concurrent.ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          @NotNull TimeUnit unit,
                          @NotNull BlockingQueue<Runnable> workQueue,
                          @NotNull ThreadFactory threadFactory,
                          @NotNull RejectedExecutionHandler handler)
  • 七大参数:

​ 1.corePoolSize – 要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut
​ 2.maximumPoolSize – 池中允许的最大线程数
​ 3.keepAliveTime – 当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。
​ 4.unit – keepAliveTime参数的时间单位
​ 5.workQueue – 用于在执行任务之前保存任务的队列。 这个队列将只保存execute方法提交的Runnable任务。
​ 6.threadFactory – 执行程序创建新线程时使用的工厂
​ 7.handler – 执行被阻塞时使用的处理程序,因为达到了线程边界和队列容量

  • 异常:

IllegalArgumentException – 如果以下情况之一成立: corePoolSize < 0 keepAliveTime < 0 maximumPoolSize <= 0 maximumPoolSize < corePoolSize

  • 4种拒绝策略:

在这里插入图片描述

四、封装线程池工具类


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;
import java.util.concurrent.*;

/**
 * 使用线程池管理线程的工具类
 */
public class ThreadPoolUtils {
    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolUtils.class);

    private static ExecutorService threadPool = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            100,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(10000));

    /**
     * 不需要返回值,不需要等待执行结果的异步任务
     */
    public static void execute(Runnable task) {
        try {
            threadPool.execute(task);
        } catch (Exception ex) {
            logger.error("ThreadPoolUtils.execute方法异常", ex);
        }
    }

    /**
     * 需要返回值,需要等待执行结果的异步任务
     */
    public static <T> Optional<Future<T>> submit(Callable<T> task) {
        try {
            return Optional.of(threadPool.submit(task));
        } catch (Exception ex) {
            logger.error("ThreadPoolUtils.submit方法异常", ex);
        }

        return Optional.empty();
    }
}

五、测试例子

1.创建任务

创建一个被执行的 Task,实现 Runnable 接口,sleep 模拟任务的执行时间是1s

/**
 * 被执行的任务
 */
@Slf4j
class Task implements Runnable {

  private final String name;

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

	@Override
  @SneakyThrows
	public void run() {
	  
    log.info("start task " + name);
    Thread.sleep(1000);
    log.info("end task  " + name);
    
	}
}

2.测试单个线程的线程池

 /**
   * 单个线程的线程池
   */
  private void singleThreadPool() {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 6; i++) {
      executorService.submit(new Task("singleThreadPool " + i));
    }
    executorService.shutdown();
  }

输出:

2021-10-14 17:50:25 [INFO] com.zhangjian.learnjava.Client: ------- start the client ------- 
2021-10-14 17:50:25 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 0 
2021-10-14 17:50:26 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 0 
2021-10-14 17:50:26 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 1 
2021-10-14 17:50:27 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 1 
2021-10-14 17:50:27 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 2 
2021-10-14 17:50:28 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 2 
2021-10-14 17:50:28 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 3 
2021-10-14 17:50:29 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 3 
2021-10-14 17:50:29 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 4 
2021-10-14 17:50:30 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 4 
2021-10-14 17:50:30 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 5 
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Task: end task  singleThreadPool 5 
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Client: ------- stop the client ------- 
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Client: client shutdown. 

测试结果:单个线程的线程池一个任务结束后下一个任务才开始

3.固定大小的线程池

 /**
   * 固定大小的线程池
   */
  private void fixThreadPool() {

    ExecutorService executorService = Executors.newFixedThreadPool(4);

    for (int i = 0; i < 6; i++) {
      executorService.submit(new Task("fixThreadPool " + i));
    }

    // 使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭。shutdownNow()会立刻停止正在执行的任务,awaitTermination()则会等待指定的时间让线程池关闭。
    executorService.shutdown();
  }

输出:

2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Client: ------- start the client ------- 
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 3 
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 2 
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 1 
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 0 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 1 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 0 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 2 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 3 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 5 
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 4 
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 5 
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Task: end task  fixThreadPool 4 
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Client: ------- stop the client ------- 
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Client: client shutdown. 

测试结果:固定大小的线层池任务释放后后面的线程才能执行

4.无界线程池

 /**
   *  无界线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE
   */
  private void cachedThreadPoll() {
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < 6000; i++) {
      executorService.submit(new Task("threadPoolExecutor " + i));
    }
    executorService.shutdown();
  }

测试结果:无界线程池一次会拉起所有的 task

5.自定义线程数量线程池

  /**
   * 自定义线程数量线程池
   */
  private void threadPoolExecutor() {
    int core = 2;
    int max = 6;
    ExecutorService executorService = new ThreadPoolExecutor(core, max, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(5));
    for (int i = 0; i < 100; i++) {
      executorService.submit(new Task("threadPoolExecutor " + i));
    }
    log.info("executorService shutdown.");
    executorService.shutdown();
  }

输出:

2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Client: ------- start the client ------- 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 0 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 1 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 9 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 8 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 7 
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 10 
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1188e820 rejected from java.util.concurrent.ThreadPoolExecutor@2f490758[Running, pool size = 6, active threads = 6, queued tasks = 5, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.zhangjian.learnjava.Client.threadPoolExecutor(Client.java:66)
	at com.zhangjian.learnjava.Client.main(Client.java:27)
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 8 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 9 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 0 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 2 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 3 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 4 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 1 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 5 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 7 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 10 
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 6 
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 3 
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 2 
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 5 
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 4 
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task  threadPoolExecutor 6 

测试结果:任务达到最大线程数+阻塞队列数后阻塞不能提交,有拒绝策略决定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值