• 多线程---线程池



    参考博客1:http://www.cnblogs.com/exe19/p/5359885.html
    参考博客2:http://www.jianshu.com/p/87bff5cc8d8c

    1 . 线程池的体系结构

    java.util.concurrent.Executor          [I]是一个顶层接口,在它里面只声明了一个方法void execute(Runnable command);
    |-- ExecutorService                    [I]继承了Executor,线程池的主要接口
        |-- AbstractExecutorService        [A]抽象类,实现了ExecutorService中的大部分方法
            |-- ThreadPoolExecutor         [C]线程池的主要实现类
        |-- ScheduledExecutorService       [I]继承了ExecutorService,负责线程调度的接口
            |-- ScheduledThreadPoolExecutor[C]继承 ThreadPoolExecutor,实现 ScheduledExecutorService

    2 .线程池的使用

    public static void method1() {
            Executor pool = Executors.newFixedThreadPool(5);
            for(int i=0;i<5;i++){
                pool.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName());
                    }
                });
            }
        }
    1、Executors.newFixedThreadPool(5)初始化一个包含5个线程的线程池pool;
    2、通过pool.execute方法提交1个任务,该任务打印当前的线程名;
    3、负责执行任务的线程的生命周期都由Executor框架进行管理;

    3 . ThreadPoolExecutor类

    • 构造方法
      public ThreadPoolExecutor(int corePoolSize,
                                int maximumPoolSize,
                                long keepAliveTime,
                                TimeUnit unit,
                                BlockingQueue<Runnable> workQueue,
                                ThreadFactory threadFactory,
                                RejectedExecutionHandler handler) {
          if (corePoolSize < 0 ||
              maximumPoolSize <= 0 ||
              maximumPoolSize < corePoolSize ||
              keepAliveTime < 0)
              throw new IllegalArgumentException();
          if (workQueue == null || threadFactory == null || handler == null)
              throw new NullPointerException();
          this.corePoolSize = corePoolSize;
          this.maximumPoolSize = maximumPoolSize;
          this.workQueue = workQueue;
          this.keepAliveTime = unit.toNanos(keepAliveTime);
          this.threadFactory = threadFactory;
          this.handler = handler;
      }
      • corePoolSize:线程池中的核心线程数。在创建了线程池后,默认情况下,线程池中的线程数为0即线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务 ( 除非调用了prestartAllCoreThreads()或者prestartCoreThread()这2个预创建线程的方法,这两个方法会在没有任务到来之前就创建corePoolSize个线程或者一个线程 )当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到阻塞缓存队列当中。
      • maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
      • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
      • unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
        TimeUnit.DAYS;              //天
        TimeUnit.HOURS;             //小时
        TimeUnit.MINUTES;           //分钟
        TimeUnit.SECONDS;           //秒
        TimeUnit.MILLISECONDS;      //毫秒
        TimeUnit.MICROSECONDS;      //微妙
        TimeUnit.NANOSECONDS;       //纳秒
      • workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
        1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
        2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
        3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
        4、priorityBlockingQuene:具有优先级的无界阻塞队列;
        其中ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
      • threadFactory:线程工厂,主要用来创建线程;
      • handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
        1、AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认策略;
        2、DiscardPolicy:也是丢弃任务,但是不抛出异常。
        3、DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
        4、CallerRunsPolicy:用调用者所在的线程来执行任务;
        当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
      • execute()方法

        public void execute(Runnable command) {
              if (command == null)
                  throw new NullPointerException();
              /*
               * Proceed in 3 steps:
               * 此过程分为3步:
               * 1. If fewer than corePoolSize threads are running, try to
               * start a new thread with the given command as its first
               * task.  The call to addWorker atomically checks runState and
               * workerCount, and so prevents false alarms that would add
               * threads when it shouldn't, by returning false.
               * 1.如果正在运行的线程数小于corePoolSize,则尝试启动一个新
               * 的线程来执行指定的任务。对addWorker 方法的调用会原子性
               * 的检查runState (运行状态)和workerCount(工作集数量 ),
               * 通过返回false阻止不应该出现的添加错误。
               *
               * 2. If a task can be successfully queued, then we still need
               * to double-check whether we should have added a thread
               * (because existing ones died since last checking) or that
               * the pool shut down since entry into this method. So we
               * recheck state and if necessary roll back the enqueuing if
               * stopped, or start a new thread if there are none.
               * 2. 如果一个线程可以成功加入队列,无论我们已经将线
               * 程添加进去了还是在进入这个方法时线程池已经关闭了都需
               * 要继续做两次检查(因为上次检查之后可能存在一个死掉的
               * 线程)所以就再次检查状态,并且如果必要就会滚加入队列的
               * 操作或者在一个也没有的时候启动一个新的线程。
               * 
               * 3. If we cannot queue task, then we try to add a new
               * thread.  If it fails, we know we are shut down or saturated
               * and so reject the task.        
               * 如果任务无法加入队列,就尝试添加一个新的线程,如果失败,
               * 说明线程池关闭了或者队列已经满了所以拒绝了这次任务
               */
              int c = ctl.get();
              if (workerCountOf(c) < corePoolSize) {
                  if (addWorker(command, true))
                      return;
                  c = ctl.get();
              }
              if (isRunning(c) && workQueue.offer(command)) {
                  int recheck = ctl.get();
                  if (! isRunning(recheck) && remove(command))
                      reject(command);
                  else if (workerCountOf(recheck) == 0)
                      addWorker(null, false);
              }
              else if (!addWorker(command, false))
                  reject(command);
          }
      • submit()方法
        此方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果

      • shutdown()和shutdownNow()方法
        这两个方法是用来关闭线程池的。

  • 相关阅读:
    朴素贝叶斯小结
    Logistic回归小结
    线性回归小结
    Zabbix使用采坑记录
    cookie与session测试关注点
    JMeter笔记十二:逻辑控制器之事务控制器、模块控制器和吞吐量控制器
    JMeter笔记十一:逻辑控制器之随机控制器、随机顺序控制器和交替控制器
    JMeter笔记十:逻辑控制器之ForEach控制器
    JMeter笔记九:逻辑控制器之循环控制器、While控制器
    JMeter笔记八:逻辑控制器之如果(If)控制器、仅一次控制器、Switch控制器
  • 原文地址:https://www.cnblogs.com/pepper7/p/7196932.html
Copyright © 2020-2023  润新知