为什么要有线程池?

不使用线程池的话 ,需要频繁的创建和销毁线程,浪费系统资源。这时可以创建一个线程池,其中维护一些线程,需要用到线程时去线程池中取,用完后还回线程池供后续线程池使用。与数据库连接池思想类似

创建线程池

Java中提供了一套Executor框架用于生成线程池。
可以通过Excutors类提供的创建几个不同性质线程池的方法:

  1. public static ExecutorService newFixedThreadPool(int nThreads)
    创建一个固定大小的线程池,需要新线程时,如果线程池中还有线程,直接拿来使用,线程不够则加入任务队列等待。
  2. public static ExecutorService newSingleThreadExcutor();
    线程池中只有一个线程,可以看成时newFixedThreadExcutor的简化版
  3. public static ExecutorService newCacheThreadPool();
    创建一个可以根据实际情况自主调节的线程池。
  4. public static ScheduledExcutorService newScheduledThreadPool(int corePoolSize)
    创建一个可调度的单线程线程池。
  5. public static ScheduledExecutorService newSingleThreadScheduledExcutor();
    创建一个可调度的线程池。

ScheduledExcutorService(计划任务)介绍

ScheduledExcutorService是对ExcutorService接口的扩展,新增加了几个方法使线程能在给定时间内执行。
用其中三个方举例:
schedule(Runnable command,long delay,TimeUnit unit)
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)

Excutors提供的这几个核心线程池的内部实现

可以看到内部的几个类都是调用了ThreadPoolExcutor方法

七个参数

  1. corePoolSize(核心线程数)
  2. MaximumPoolSize(最大线程数)
  3. keepAliveTime(超过核心线程数后,多与线程的存活时间)
  4. unit(时间单位)
  5. workQueue(任务队列)
  6. threadFactory(线程工厂)
  7. handler(拒绝策略)

核心线程池的调用参数

newFixedThreadPool:调用的参数分别为(nthreads,nthreads,0,TimeUnit.MilliSeconds,new LinkedBlockingQueue());
newSingleThreadExcutor:只是把前面的两个参数变为1。
newCachedThreadPool:
(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new Synchornous())

任务队列

是BLOCKEDQUEUE接口的对象
4大类:

  1. SynchronoueQueue:容量为零,有新任务时不会将新任务保存,而是直接将任务提交给线程执行,若没有空闲线程,则新建线程,线程数达到最大数则执行拒绝策略
  2. ArrayBlockingQueue:需要在初始化时任务队列的大小,有新任务来时,若实际线程数小于corePoolSize,将任务提交给新线程做。 若>corePoolSize则放入任务队列中。任务队列满后,尝试建立新线程,线程数量大于最大线程数,执行拒绝策略。
  3. LinkedBlockingQueue:大小无上限的任务队列,线程小于corePoolSize时,会优先创建新线程执行任务。但线程数大于corePoolSize时,将任务放入任务队列中,若任务的创建和处理时间差距过大,则会导致队列无限增长,直至内存耗尽
  4. PriorityBlockingQueue:上述几个任务队列都是按先入先出的原则,而优先队列会优先执行优先级高的

拒绝策略

JDK预置了4种拒绝策略:

  1. AbortPolicy:直接报错,终止任务
  2. CallerRunsPolicy:将无法执行的线程交给调用者线程执行
  3. DiscardOldestPolicy:抛弃最老的请求,也就是即将执行的任务,并且再次尝试提交当前任务
  4. DiscardPolicy:直接丢弃线程不与任何处理。

这几个拒绝策略均实现了RejectedExecutionHandler接口,该接口内有一个方法rejectedExecution(Runnable r,ThreadPoolExecutor executor),可以通过重写该方法来自定义拒绝策略

线程工厂

ThreadFactory是一个接口,内含有一个创建线程的方法newThread(Runnable r)

Q.E.D.