原理

Executor

  • 执行者,顶层接口
  • 线程池从功能上看就是一个任务执行器
  • ThreadPoolExecutor提交任务逻辑
    • 判断corePoolSize,如果没到则新建,并标记为核心线程
    • 如果达到corePoolSize,则加入workQueue
    • 判断maximumPoolSize,如果超过执行拒绝策略处理器RejectedExecutionHandler ```java void execute(Runnable command); // 无返回值,主线程不能捕获异常 Future<?> submit(Runnable task); // 有返回值,主线程可以捕获异常
  1. <a name="KVlvG"></a>
  2. ## ExecutorService
  3. - 接口API
  4. - 启动
  5. - 关闭等
  6. ```java
  7. void execute(Runnable command); // 无返回值,主线程不能捕获异常
  8. void shutdown(); // 停止接收新任务,原来的任务继续执行
  9. List<Runnable> shutdownNow(); // 立即关闭,包括原来的任务
  10. awaitTermination(long timeOut, TimeUnit unit); // 当前线程阻塞

ThreadFactory

  • 线程工厂

    • 批量创建线程
    • 批量设置属性
      Thread newThread(Runnable r); // 创建新线程
      

      Executors

  • 工具类

  • 线程池的工厂

    Callable

  • Runnable#run()没有返回值

  • Callable#call()有返回值

    public class RandomSleepTask implements Callable<Integer>{
      @Override
      public Integer call() throws Exception{
          Integer sleep = new Random().nextInt(10000);
          TimeUnit.MILLISECONDS.sleep(sleep);
          return sleep;
      }
    }
    

    Future

    Future.png

    线程池参数

    缓冲队列

    BlockingQueue是双缓冲队列,内部使用两条队列,允许两个线程同时向队列存储、取出,保证并发安全的同时,提高了队列的存取效率

  • ArrayBlockingQueue:规定大小的BQ,其构造必须指定大小,所含的对象是FIFO顺序排序的

  • LinkedBlockingQueue:大小不固定的BQ,若其构造时指定大小,生成的BQ有大小限制,不指定大小则有Integer.MAX_VALUE来决定,所含的对象是FIFO顺序排序的
  • PriorityBlockingQueue:类似于LinkedBlockingQueue,但是所含对象的排序不是FIFO,而是依据构造函数的Comparator或自然顺序决定
  • SynchronizedQueue:特殊的BQ,对其操作必须是放和取交替完成

    拒绝策略

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认策略

  • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

    线程工厂

    public class CustomThreadFactory implements ThreadFactory{
      private AtomicInteger serial = new AtomicInteger(0);
    
      @Override
      public Thread newThread(Runnable r){
          Thread thread = new Thread(r);
          thread.setDaemon(true); //根据需要
          thread.setName("CustomThread-"+serial.getAndIncrement());
          return thread;
      }
    }
    

    创建线程池方法

  • newSingleThreadExecutor

    • 创建一个单线程的线程池,其实就是单线程串行执行所有任务保,证所有的执行顺序按照提交顺序依次执行
    • 如果这个线程异常结束,那么就会有一个新的线程来替代它
  • newFixedThreadPool
    • 创建固定大小的线程池
    • 每次提交一个任务就创建一个线程,直到最大大小,达到最大值后就会保持不变
    • 如果某个线程因为异常而结束,那么线程池就会补充一个新线程
    • 创建固定线程池的经验
      • 不是越大越好,太小肯定也不好,假设核心数是N
      • 如果是CPU密集型,则线程池大小设置为N或N+1
      • 如果是IO密集型,则线程池大小设置为2N或2N+2
  • newCachedThreadPool
    • 创建一个可缓存的线程池
    • 如果大小超过了处理任务需要的线程就会回收部分空闲(60s不执行)的线程
    • 当任务数增加时,又可以智能的增加新线程来处理任务
    • 不会对线程池大小做限制,完全依赖于JVM能够创建的最大线程大小
  • newScheduledThreadPool
    • 创建一个大小无限的线程池
    • 支持定时以及周期性的执行任务的需求