线程池底层原理介绍

池化思想

优点

  1. 提高线程利用率
  2. 提高响应速度
  3. 可以控制最大并发数
  4. 便于统一管理

参数

参数

解释

corePoolSize

核心线程数量,线程池维护线程的最少数量

maximumPoolSize

线程池维护线程的最大数量

keepAliveTime

线程池除核心线程外的其他线程的最长空闲时间,超过该时间的空闲线程会被销毁

unit

keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS

workQueue

线程池所使用的任务缓冲队列

threadFactory

线程工厂,用于创建线程,一般用默认的即可

handler

线程池对拒绝任务的处理策略

处理过程

1.创建一个线程池,线程池里有多个核心线程,当任务进来时,当前线程数量小于corePoolSize时,会立刻创建线程不管有米有空闲线程;
2.当任务进来后,如果有空闲的线程,则会立刻调用线程,如果没有空闲的线程就会将任务放到一个阻塞队列中,直到有空闲的线程出现;
3.当阻塞队列满时,线程池才会尝试创建新的线程,如果线程数量大于最大线程数,则会采取拒绝任务的策略
4.当后来创建的线程空闲达到一定时间后,会自动销毁,直到满足核心线程数。

WorkQueue

主要有四类:

  1. ArrayBlockingQueue:有界阻塞队列,一般使用的这个,当任务量大于队列长度时,要么创建线程要么拒绝任务
  2. LinkedBlockingQueue:无界阻塞队列,maxmumPoolSize无效,会一直加入线程,不会创建新的线程去处理任务
  3. SynchronousQueue:同步队列,不能存放任务,一旦有任务进来,必须创建线程来处理任务
  4. PriorityBlockingQueue:优先阻塞队列,具有优先级的无界阻塞队列

Handler

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;也是默认的处理方式。
  2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

常见的线程池

  1. Executors.newCachedThreadPool
    new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());
    使用SynchronousQueue,有任务进来就创建线程,线程空闲就回收销毁
  2. Executors.newFixedThreadPool
    new ThreadPoolExector(n Threads,n Threads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())
    线程数固定,不能创建新的线程,如果有任务进来就阻塞
  3. Executors.newSingleThreadExecutor();
    new ThreadPoolExector(n Threads,n Threads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。
  4. Executors.newScheduledThreadPool(int);
    new ScheduledThreadPoolExecutor(corePoolSize)
    创建一个定长线程池,支持定时及周期性任务执行。

核心线程数选择

cpu密集型任务:系统的I/O读写效率高于CPU效率,大部分的情况是CPU有许多运算需要处理,使用率很高。但I/O执行很快。那么核心线程数为cpu+1
IO密集型任务:系统的CPU性能比磁盘读写效能要高很多,大多数情况是CPU在等I/O的读写操作,此时CPU的使用率并不高;核心线程数为cpu*2
混合型任务:既包含CPU密集型又包含I/O密集型。核心线程数为(线程等待时间/线程cpu时间+1)*cpu

核心线程数过多或者过少的问题

核心线程数过多会导致线程之间竞争激烈,上下文切换频繁,从而影响整体效率
核心线程数过少会导致大量任务进来后,会导致任务处于阻塞状态需要长时间等待,或者队列满了之后有任务无法被执行,甚至导致内存溢出(OOM)。

创建线程池的方式

  1. 使用Executors工具类
  2. 使用ThreadPoolExector类去创建

execute()方法和submit()方法的区别

execute()方法提交的是没有返回值的任务,无法判断该任务时候被线程池执行
submit()方法提交的是有返回值的任务,返回类型是future类,通过future类的get()方法获取返回值时会阻塞当前线程直到得到结果,get(long timeout,TimeUnit unit)方法是阻塞一段时间后返回,任务不一定完成,也有可能完成。cancel方法可以取消任务,已经完成或者还未开始任务则返回false,任务正在执行则返回true并且中断任务。

线程池调优

核心线程数选择
最大线程数设置,防止县城资源耗尽
使用有界的阻塞队列,提高系统的稳定性和预警能力