多线程的相关知识点总结

172 阅读3分钟

创建线程的几种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口(有返回值)
  4. 线程池创建
线程池的实现方式
  • Executors.newFixedThreadPool(5); 一池固定线程:固定大小线程池,适用于任务数量固定,执行时间较长的任务
  • Executors.newSingleThreadExecutor(); 一池一线程:单线程池,可以保证任务的顺序性
  • Executors.newCachedThreadPool(); 一池多线程:可以无限扩大,适合任务量大,但是执行时间很短的线程,这种线程不会造成CPU的过度切换
  • newScheduledThreadPool:适用于执行延迟或者周期性的任务

那么实际使用就是这样的
ExecutorService es=new ThreadPoolExecutor(7个参数);


参数解释:

  1. corePoolSize: 线程池常驻核心线程数
  2. maxImumPoolSize: 线程池能够容纳同时执行的最大线程数,必须大于等于1
  3. keepAliveTime: 多余的空闲线程的存活时间
  4. unit: keepAliveTime的单位
  5. workQueue: 任务队列,被提交但尚未被执行的任务
  6. threadFactory: 线程池线程工厂,一般用默认的
  7. handler: 拒绝策略,当队列满了并且工作线程大于等于线程池的最大线程数 maxImumPoolSize 会执行拒绝策略


4种拒绝策略:

  1. AbortPolicy(默认):直接抛出RejectedExecutionException 异常组织系统正常运行
  2. CallerRunsPolicy:该策略不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
  3. DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
  4. DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这种策略是可行的

1、4都好理解。第2种解释一下:我去A银行办理业务,A银行人员说这里满了,让我去B银行处理;到了B银行,B银行说这里也满了,谁叫你来的你叫谁处理,所以我又回到了A银行等待办理。


核心线程数如何决定:

最大线程数=max+阻塞队列数

最大线程数参数应该怎么设置?
需要看具体业务是做什么,分为CPU计算密集型和IO密集型。
先查看机子是几核的,再根据具体类型进行计算
Runtime.getRuntime().availableProcessors();

CPU密集型
  • 一般公式:CPU核心数+1个线程的线程池
IO密集型

线程的创建必须通过线程池:



线程池的工作原理

通俗点来说:
核心线程数:内部员工;阻塞队列:任务仓库;非核心线程:外包人员,仓库堆满了,任务办不完,让外包人员做, 如果外包人员达到最大数量,还是做不完,就交给拒绝策略

工作原理流程图

右半部分流程用文字描述:

1.在创建了线程池后,等待提交过来的任务请求

2.当调用execute() 方法添加一个请求任务时线程池会进行判断:

  2.1 如果正在运行的线程数量小于corePollSize,那么马上创建线程运行这个任务;

  2.2 如果正在运行的线程数量大于或等于corePoolSize,那么任务会放入队列;

  2.3 如果队列满了并且运行的线程数量小于 最大线程数(maximunPoolSize),那么会创建非核心线程运行任务

  2.4 如果队列满了并且运行的线程数量大于等于最大线程数,那么就执行拒绝策略 

3. 当线程空出来无事可做的时候超过一定的时间(keepAliveTime),线程池会判断如果当前运行的线程数量大于核心线程数(corePoolSize),那么空出来的线程会被停掉