多线程与高并发编程(八)【线程池二】

多线程与高并发编程(八)【线程池二】

    • 一、线程池
      • 1. SignleThreadPool
      • 2. CachedThreadPool
      • 3. FixedThreadPool
      • 4. ScheduledThreadPool
      • 5. WorkStealingPool
      • 6. ParallelSteamAPI
    • 二、ThreadPoolExecutor源码观后感
      • 1. worker类:
      • 2. submit方法
      • 3. execute方法:
      • 4. addWorker方法:

一、线程池

1. SignleThreadPool

  单线程的线程池。为什么要有单线程的线程池:需要维护一个任务队列、生命周期管理。
  Executors.newSingleThreadExecutors();
在这里插入图片描述
  Executors 线程池的工厂(就像Arrays是list的工厂,Collections是容器的工厂)
  阿里开发手册要求不要使用JDK自带的实现来定义线程池,不推荐这个,因为LinkedBlockingQueue队列是有上界的,integer.max,越积越多最后会OOM;到了上界之后JDK默认的拒绝策略在生产环境可能会出问题;线程名称的问题,所以需要自定义。
  源码:
多线程与高并发编程(八)【线程池二】_第1张图片

2. CachedThreadPool

  来一个任务直接就进入线程池中找线程,若没有空闲线程则new一个线程在池中处理,上限是integer.max,因为队列是synchronusQueue所以容量为0,所以来了任务必须直接处理。
  Executors.newCachedThreadPool();
  阿里中不推荐原因是可能会起非常多的线程。
  源码:
多线程与高并发编程(八)【线程池二】_第2张图片

3. FixedThreadPool

  指定参数,一共有多少个线程
  阿里中不推荐原因是同singleThreadPool
  源码:
多线程与高并发编程(八)【线程池二】_第3张图片

  Cached VS Fixed:
    可估算、线程数比较平稳的用fixed。
    阿里都不用,自己估算,进行精确定义

4. ScheduledThreadPool

  定时任务线程池,这个队列可以让任务每搁多长时间运行。
  时间可以用quartz框架、cron框架(复杂的时间框架,不复杂可以用Timer)。
  .scheduleAtFixedRate(callable, initialDelay, period, TimeUnit)   【initialDelay:第一个任务执行之前需要等多长时间。period:每隔多长时间。最后一个单位】
多线程与高并发编程(八)【线程池二】_第4张图片
  源码:
在这里插入图片描述
多线程与高并发编程(八)【线程池二】_第5张图片
  面试题(开发题没标准答案):假如提供一个闹钟服务,订阅这个服务的人特别多,十亿人,怎么优化?
  答:比如分发任务到很多边缘服务器,每个服务器上用线程池和队列。
  并行与并发的区别:
    并发:concurrent 指任务提交,从人的角度看多个任务涌过去是并发,实际上可能是一个cpu不同的顺序处理。
    并行:parallel 指任务执行,真正意义上多个cpu同时处理。
    并行是并发的子集

  总结:以上四种线程池底层实现都是利用ThreadPoolExecutor

  手写拒绝策略:
    自定义个类实现RejectedExecutionHandler,重写方法内写入想要的操作。
  下图自定义思路:
    log生成日志,save保存日志到哪,尝试n次放回池子,如果队列数量小于n(自定义的队列数量),则再试一下放入。
多线程与高并发编程(八)【线程池二】_第6张图片

5. WorkStealingPool

  每个线程都在池内有一个自己的任务队列,当队列中空了之后去别的线程的队列那去偷一个拿来执行。

  源码:
多线程与高并发编程(八)【线程池二】_第7张图片

     实际上是利用ForkJoinPool,将大人物进行拆分处理最后汇总(比如1亿个数相加)。
    因为任务需要可以拆分,所以与其他的池通过runnable不同,有两种实现:
  ①需要继承RecursiveAction类(没有返回值)
多线程与高并发编程(八)【线程池二】_第8张图片
    compute之中定义AddTask(开始位置,结束位置)调用.fork()执行分叉
多线程与高并发编程(八)【线程池二】_第9张图片
    主方法执行:
多线程与高并发编程(八)【线程池二】_第10张图片
  ②继承自RecursiveTask
compute方法(这次的类叫AddTaskRet):
多线程与高并发编程(八)【线程池二】_第11张图片
    主方法:
多线程与高并发编程(八)【线程池二】_第12张图片

6. ParallelSteamAPI

  底层也是利用ForkJoinPool实现。
  下图利用Lambda表达式,T13_ParallerlSteamAPI为当前类。
  isPrime()是一个自定义的方法,里面是自己写的判断是否为质数的逻辑。
在这里插入图片描述
在这里插入图片描述

二、ThreadPoolExecutor源码观后感

  总结流程:

1. worker类:

  本身是一个Runnable也是一个AQS
  Runnable是为了记录一些原本没有的东西。
  AQS是因为worker会被多线程访问,本身直接就做成了一把锁。
  成员变量有个Thread记录哪个线程抢到了。

2. submit方法

3. execute方法:

  核心线程数不到最大核心数则起一个核心线程处理。
  核心线程数最大了则放入队列。
  上面两个都满了,起非核心线程处理。

4. addWorker方法:

  干了两件事:
    ①count++ 线程数量先加1
    ②真正加了一个线程处理并启动

你可能感兴趣的:(多线程与高并发)