简述线程池 以及四种常见线程池

线程池

  • 概念

    线程池就是首先创建一些线程,他们的集合称为线程池,使用线程池可以很好的提高性能。

  • 5种状态

    ThreadPoolExecutor 使用int的高3位来表示线程池状态,低29位表示线程数量

    状态名高3位说明
    Running111(这里的第一个1是负数)正在运行状态
    ShutDown000不接收新的任务,但会处理阻塞队列中剩余的任务
    Stop001不接收新的任务,并抛弃阻塞队列中的剩余任务
    Tidying010任务全执行完毕,活动线程为0即将进入终结状态
    Terminated011终结状态

    这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一。这样就可以用一次cas原子操作进行赋值。

  • 构造方法 (7大参数)

    public ThreadPoolExecutor(int corePoolSize,    //核心线程数目(最多保留的线程数)
                                 int maximumPoolSize,   //最大线程数目
                                 long keepAliveTime,    //生存时间-针对救急线程
                                 TimeUnit unit,         //时间单位-针对救急线程
                                 BlockingQueue<Runnable> workQueue, //阻塞队列
                                 ThreadFactory threadFactory,       //线程工厂-可以为线程创建时起个好名字
                                 RejectedExecutionHandler handler)  //拒绝策略
    
  • 工作方式

    • 线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。

    • 当线程数量达到corePoolSize(核心线程数)并没有线程空闲的时候,这时再加入任务,新加的任务会被workqueue(阻塞队列)排队,直到有空闲的线程。

    • 如果队列选择了有界队列,那么任务超过了队列大小的时候,会创建 maximumPoolSize - corePoolSize数目的线程来救急。

    • 如果线程达到了maximumPoolSize 仍然有新的任务,这时会执行拒绝策略。拒绝策略jdk提供了4中实现,其他著名框架也提供了实现。

      记不住了。。。。。。。。先放着吧

      AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略。

      CallerRunsPolicy 让调用者运行任务
      DiscardPolicy 放弃本次任务
      DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之

      Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方便定位问题
      Netty 的实现,是创建一个新线程来执行任务
      ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
      PinPoint 的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略

    • 当高峰过去之后,超过corePollSize的救急线程如果一段时间没有任务做。需要结束节省资源,这个时间由keepAliveTime和unit来控制。

在这里插入图片描述
根据这个构造方法,JDK Executors类中提供了众多工厂方法来创建各种用途的线程池。
在这里插入图片描述

  • 四种常见线程池

    • 可缓存线程池 newCachedThreadPool

      =>(先查看池中有没有以前建立的线程,如果有就直接使用,如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步性任务)

      public static ExecutorService newCachedThreadPool() {
      	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
      			new SynchronousQueue<Runnable>());
      }
      

      特点:

      • 核心线程数是0,最大线程数是Integer.MAX_VALUE,救急线程的空闲生存时间是60s,意味着

        =>全部都是救急线程(60s后可以回收)

        =>救急线程可以无限创建

      • 队列采用了SynchronizedQueue实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)。

      • 整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕。空闲1分钟后释放线程。适合任务数比较密集,但每个任务执行时间较短的情况。

      • 代码测试

        public class CachedThreadPool {
            public static void main(String[] args) {
                //创建一个可缓存的线程池
                ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    cachedThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            //打印正在执行的缓存线程信息
                            System.out.println(Thread.currentThread().getName()+"正在被执行");
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
        
                    });
                }
            }
        }
        结果:
        pool-1-thread-1正在执行的线程
        pool-1-thread-2正在执行的线程
        pool-1-thread-2正在执行的线程
        pool-1-thread-1正在执行的线程
        pool-1-thread-1正在执行的线程
        pool-1-thread-2正在执行的线程
        pool-1-thread-2正在执行的线程
        pool-1-thread-1正在执行的线程
        pool-1-thread-2正在执行的线程
        pool-1-thread-1正在执行的线程
        
    • newFixedThreadPool (固定大小的线程池)

      public static ExecutorService newFixedThreadPool(int nThreads) {
      	return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
      		   new LinkedBlockingQueue<Runnable>());
      }
      

      特点:

      ​ =>核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间,

      ​ =>阻塞队列是无界的,可以放任意数量的任务

      ​ =>适用于任务量已知,相对耗时的任务

      代码测试:

      public class FixedThreadPool {
          public static void main(String[] args) {
              //创建一个可重用固定个数的线程池
              ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
              for (int i = 0; i < 10; i++) {
                  fixedThreadPool.execute(new Runnable() {
                      @Override
                      public void run() {
                          System.out.println(Thread.currentThread().getName()+"正在被执行");
                          try {
                              Thread.sleep(1000);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  });
              }
          }
      }
      结果:
          其实它是一组一组执行的,因为线程池大小为3,每次输出睡1秒,所以每一秒打印3个结果。
          定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
          
      pool-1-thread-1正在被执行
      pool-1-thread-2正在被执行
      pool-1-thread-3正在被执行
          
      pool-1-thread-2正在被执行
      pool-1-thread-3正在被执行
      pool-1-thread-1正在被执行
          
      pool-1-thread-2正在被执行
      pool-1-thread-3正在被执行
      pool-1-thread-1正在被执行
          
      pool-1-thread-2正在被执行	
      
    • newSingleThreadPool(单例线程池)

      =>他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序FIFO,LIFO,优先级执行)

      public static ExecutorService newSingleThreadExecutor() {
      	return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, 							TimeUnit.MILLISECONDS,
      		new LinkedBlockingQueue<Runnable>()));
      }
      
      • 使用场景

        希望多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队。

        任务执行完毕,这唯一的线程也不会被释放。

      • 区别:

        =>自己创建一个单线程串行执行任务,如果任务失败而终止那么没有任何补救措施,而线程池还会创建一个线程,保证池的正常工作

        =>Executors.newSingleThreadExecutor()线程个数始终为1,不能修改

        ​ FinalizableDelegatedExecutorService应用的是装饰器模式。

        ​ 只对外暴露了ExectorService接口,因为不能调用ThreadPoolExector中特有的方法。

        =>Executors.newFixedThreadPool(1)初始化为1,以后还可以修改。

        ​ 对外暴露的是ThreadPoolExector对象。可以强转后调用setCorePoolSize等方法进行修改

      • 代码测试

        public class SingleThreadPool {
            public static void main(String[] args) {
                ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        
                for (int i = 0; i < 10; i++) {
                    final int index = i;
                    singleThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread().getName()+"正在被执行,打印的值是:"+index);
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
        结果:按顺序一个一个执行的
        pool-1-thread-1正在被执行,打印的值是:0
        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
        
    • newScheduledThreadPool(定长线程池)

      =>创建一个定长线程池,支持定时及周期性任务执行

      代码测试

      public class ScheduledThreadPool {
          public static void main(String[] args) {
              ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(6);
              scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("延迟1秒后每3秒执行1次");
                  }
              },1,3, TimeUnit.SECONDS);
          }
      }
      结果:
      延迟1秒后每3秒执行1次
      延迟1秒后每3秒执行1......
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值