线程池相关知识点

  1. 从阻塞队列开始说起
    1. 在操作阻塞队列时,如果队列内容为空,那么消费线程会被阻塞;如果队列已经满了,那么生产线程将会阻塞
    2. 阻塞队列的分类
      1. ArrayBlockingQueue
        1. 有界队列
        2. 底层为Array形式存储
        3. 如果所有的任务都是按顺序执行,不存在“插队”和从队伍中离开,则适合使用ArrayBlockingQueue
      2. LinkedBlockingQueue
        1. 无界队列
        2. 底层为链表形式存储
        3. 如果存在“插队”,和从队伍中离开的情况则适合使用LinkedBlockingQueue
      3. SynchronousQueue
        1. 只能有一个元素
    3. 阻塞队列的使用示例

  2. 使用线程的优势

    1. 减少了线程创建,销毁的性能消耗,复用了线程
    2. 充分利用cpu的内核(相对于单线程),省略了上下文的切换
    3. 提高了线程的可管理性,线程池可以对线程统一分配,调优和监控
    4. 由于减少了线程的创建时间,所以响应速度会有所提高
  3. 线程池的几个参数作用
    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler)
    
    1. corePoolSize:线程池的核心线程数量,如果线程池中的线程数量小于这个数,每次执行execute()方法会创建新的线程;如果正在运行的线程数大于corePoolSize,当有新的任务时候,会将任务加入到阻塞队列
    2. 当任务已经填满阻塞对列,则会创建新的线程去执行任务,直到线程数到达maximumPoolSize;maximumPoolSize就是线程池中可以创建的最大线程数;如果线程池中的线程已经超过corePoolSize,当队列中的任务逐渐减少时,线程池的中的线程也会销毁。
    3. 当线程超过keepAliveTime没有执行任务时,线程池中的线程就会销毁,直到线程数量减少到 corePoolSize;如果设置了allowCoreThreadTimeOut,核心线程也会销毁,线程池中线程数量会减少到0
    4. unit参数就是线程超时销毁的时间单位,如TimeUnit.MILLISECONDS
    5. workQueue就是暂存任务的阻塞队列
    6. threadFactory是创建线程的工厂类,一般使用默认的工厂类,Executors.defaultThreadFactory()
    7. handler 线程池的拒绝策略
      1. 在线程池中线程的数量已经达到maximumPoolSize,并且阻塞对列中已经放满了待执行的任务,那么线程池就会执行拒绝策略,拒绝新的任务
      2. 常见的拒绝策略
        1. new ThreadPoolExecutor.CallerRunsPolicy()
          1. 将新的任务交给调用者执行
        2. new ThreadPoolExecutor.AbortPolicy()
          1. 直接抛出RejectedExecutionException异常
        3. new ThreadPoolExecutor.DiscardPolicy()
          1. 丢弃调新的任务,但不抛出异常
        4. new ThreadPoolExecutor.DiscardOldestPolicy()
          1. 丢弃队列中最老的任务,但不抛出异常
  4. Executors提供的几个线程池

    就像Collectionss提供针对Collection的工具一样,Executors提供了针对Executor的各种工具方法,其中最常见的便是·创建线程的工具方法,有四种:

    //创建n个线程,n为Integer.MAX_VALUE
    Executors.newCachedThreadPool();
    //有固定线程数的线程池,corePoolSize和maxPoolSize相等
    Executors.newFixedThreadPool(10);
    //有定时或周期执行的线程池
    Executors.newScheduledThreadPool(10);
    //只有一个线程的线程池 corePoolSize = maxPoolSize = 1
    Executors.newSingleThreadExecutor();
    

    特别注意:在生产项目中一般不使用Executors提供的这几个线程池,因为它底层的阻塞队列是无限的,有可能会造成OOM

  5. 带返回值的线程池

    1. 如果要获取子线程返回的结果,那么要实现Callable接口
    2. FutureTask类实现了Runnable接口,并且拥有包含Callable参数的构造函数FutureTask(Callable task),可以通过futureTask去执行要获得返回结果的子线程
    3. 一个执行实现Callable接口的示例
      public class ThreadPoolDemo {
      
      
          public static  void main(String[] args){
      
                TaskWithReturn taskWithReturn = new TaskWithReturn();
              FutureTask<Integer> taskWithReturnFutureTask = new FutureTask<Integer>(taskWithReturn);
              Thread thread = new Thread(taskWithReturnFutureTask);
              thread.start();
          }
      
          private static class TaskWithReturn implements Callable<Integer> {
              @Override
              public Integer call() throws Exception {
                  System.out.println(Thread.currentThread().getName());
                  return 1234;
              }
          }
      }
      
  6. ThreadPoolExecutor的execute方法和submit方法区别
    1. execute只能执行实现了Runnable接口的类;submit可以执行实现了Runnable接口的类也可以执行实现了Callable接口的类
    2. submit方法可以通过返回值Future获取到子线程的执行结果和异常信息, 如果线程正确执行完毕future.get()获取到线程的返回结果,如果子线程抛出异常future.get()获取到异常信息

创作不易,转载请注明文章来源为www.full-satcker.com:邢桂帅 » 线程池相关知识点

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址