多线程-线程池解析

这里是引用


一、什么是线程池

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

二、为什么要使用线程池

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建(new线程)和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

三、线程池

线程池的7大参数

     //构造方法
    public ThreadPoolExecutor(int corePoolSize,//核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//最大空闲时间(超时了没有人调用就会释放线程)
                              TimeUnit unit,//最大空闲时间的单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,创建线程的.一般不动
                              RejectedExecutionHandler handler//拒绝策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

线程池的工作流程图

在这里插入图片描述

线程池的3大方法

1.Executors.newSingleThreadExecutor

核心思想:单个线程
newSingleThreadExecutor源码部分:

   public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
             //最大线程数和核心线程数都是1
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

代码部分:

    public static void main(String[] args) {
                                          //可以理解为单例
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for(int i = 1;i<=10;i++){
            int temp = i;
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
            });
        }

执行结果

pool-1-thread-1执行第1个任务
pool-1-thread-1执行第2个任务
pool-1-thread-1执行第3个任务
pool-1-thread-1执行第4个任务
pool-1-thread-1执行第5个任务
pool-1-thread-1执行第6个任务
pool-1-thread-1执行第7个任务
pool-1-thread-1执行第8个任务
pool-1-thread-1执行第9个任务
pool-1-thread-1执行第10个任务

2.Executors.newFixedThreadPool(int nThreads)

核心思想:创建一个固定的线程池大小
源码部分:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        //最大线程数和核心线程数由程序员自己设置
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

代码部分:

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for(int i = 1;i<=10;i++){
            int temp = i;
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
            });
        }
    }

结果:

pool-1-thread-1执行第1个任务
pool-1-thread-5执行第5个任务
pool-1-thread-4执行第4个任务
pool-1-thread-3执行第3个任务
pool-1-thread-2执行第2个任务
pool-1-thread-3执行第9个任务
pool-1-thread-4执行第8个任务
pool-1-thread-5执行第7个任务
pool-1-thread-1执行第6个任务
pool-1-thread-2执行第10个任务

3.Executors.newCachedThreadPool()

核心思想:可伸缩的,遇强则强,遇弱则弱
源码部分:

   public static ExecutorService newCachedThreadPool() {
        //核心线程数是0,最大线程数是Integer.MAX_VALUE
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                         //当线程执行完任务闲置久了就会被淘汰
                         //设置了线程的淘汰时间
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

代码部分:

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for(int i = 1;i<=100;i++){
            int temp = i;
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
            });
        }
    }

部分结果:

pool-1-thread-1执行第1个任务
pool-1-thread-2执行第2个任务
pool-1-thread-3执行第3个任务
pool-1-thread-5执行第5个任务
pool-1-thread-6执行第6个任务
pool-1-thread-4执行第4个任务
pool-1-thread-8执行第8个任务
pool-1-thread-7执行第7个任务
pool-1-thread-9执行第9个任务
pool-1-thread-4执行第14个任务
pool-1-thread-7执行第12个任务
pool-1-thread-8执行第13个任务

可以从结果看出第7,8个线程去执行了多次任务,当任务执行完毕后,根据线程池设置线程最大的闲置时间,把线程回收掉。

线程池的4大拒绝策略

在这里插入图片描述
new ThreadPoolExecutor.AbortPolicy(); // 抛出异常
new ThreadPoolExecutor.CallerRunsPolicy();// 哪来的去哪(主线程来的,就回去让主线程执行)
new ThreadPoolExecutor.DiscardPolicy();// 丢掉任务,不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();//阻塞队列满了,把最前面阻塞队列的任务丢掉,并执行当前任务,也不会抛出异常!

1.new ThreadPoolExecutor.AbortPolicy()

自己创建一个线程池:

    public static void main(String[] args) {
      ExecutorService threadPool = new ThreadPoolExecutor(
              2,//核心线程数
              5,//最大线程数
              3,//最长闲置时间
              TimeUnit.SECONDS,
              new LinkedBlockingDeque<>(3),//最多有3个任务队列
              Executors.defaultThreadFactory(),
              //拒接策略:抛出异常
              new ThreadPoolExecutor.AbortPolicy()
      );
      for(int i = 1;i<=8;i++){
          //使用了线程池之后,使用线程池来创建线程
          int temp = i;
          threadPool.execute(()->{
              System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
          });
      }
    }

八个任务正常执行

pool-1-thread-1执行了第1个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-3执行了第4个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-2执行了第5个任务
pool-1-thread-4执行了第7个任务

九个任务时,执行完前八个任务后报异常处理

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.ArrayQueueDemo$$Lambda$1/1096979270@3b9a45b3 rejected from java.util.concurrent.ThreadPoolExecutor@7699a589[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 1]
	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 com.ArrayQueueDemo.main(ArrayQueueDemo.java:20)

2.new ThreadPoolExecutor.CallerRunsPolicy()

    public static void main(String[] args) {
      ExecutorService threadPool = new ThreadPoolExecutor(
              2,
              5,
              3,
              TimeUnit.SECONDS,
              new LinkedBlockingDeque<>(3),
              Executors.defaultThreadFactory(),
              //拒接策略:哪来的去哪(主线程来的,就回去让主线程执行)
              new ThreadPoolExecutor.CallerRunsPolicy()
      );
      for(int i = 1;i<=9;i++){
          //使用了线程池之后,使用线程池来创建线程
          int temp = i;
          threadPool.execute(()->{
              System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
          });
      }
    }

核心思想:哪来的去哪(主线程来的,就回去让主线程执行)

pool-1-thread-1执行了第1个任务
pool-1-thread-4执行了第7个任务
main执行了第9个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-3执行了第5个任务
pool-1-thread-4执行了第4个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-5执行了第8个任务

3.new ThreadPoolExecutor.DiscardPolicy()

丢掉了第九个任务,不报异常

      ExecutorService threadPool = new ThreadPoolExecutor(
              2,
              5,
              3,
              TimeUnit.SECONDS,
              new LinkedBlockingDeque<>(3),
              Executors.defaultThreadFactory(),
              //拒绝策略:丢掉任务,不抛出异常
              new ThreadPoolExecutor.DiscardPolicy()
      );
      for(int i = 1;i<=9;i++){
          //使用了线程池之后,使用线程池来创建线程
          int temp = i;
          threadPool.execute(()->{
              System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
          });
      }
    }
pool-1-thread-1执行了第1个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-4执行了第7个任务
pool-1-thread-3执行了第4个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-2执行了第5个任务

4、new ThreadPoolExecutor.DiscardOldestPolicy()

    public static void main(String[] args) {
      ExecutorService threadPool = new ThreadPoolExecutor(
              2,
              5,
              3,
              TimeUnit.SECONDS,
              new LinkedBlockingDeque<>(3),
              Executors.defaultThreadFactory(),
              //拒绝策略:阻塞队列满了,把最前面阻塞队列的任务丢掉,并执行当前任务,也不会抛出异常!
              new ThreadPoolExecutor.DiscardOldestPolicy()
      );
      for(int i = 1;i<=9;i++){
          //使用了线程池之后,使用线程池来创建线程
          int temp = i;
          threadPool.execute(()->{
              System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
          });
      }
    }

可以看出把第三个任务给挤掉了去执行了第九个任务

pool-1-thread-1执行了第1个任务
pool-1-thread-4执行了第7个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-5执行了第9个任务
pool-1-thread-4执行了第5个任务
pool-1-thread-1执行了第4个任务

四、总结

线程池的设计思路为:线程池为一个创建管理线程的工厂,把执行任务分配给各个线程去执行,当线程数量不满足任务数量时。会暂时把任务防止阻塞队列中,等待空闲线程来执行。当阻塞队列放置满了,就会创建新线程去执行多出的任务。当任务足够多,以至于创建的线程达到最大线程数时,就会采用拒绝策略不接收任务。最后,当任务量小了,大部分线程执行完任务后,转变为待执行的线程时。会清理掉一些线程数量,以保证线程利用率和降低资源开销。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值