1. 从阻塞队列开始说起
  1. 在操作阻塞队列时,如果队列内容为空,那么消费线程会被阻塞;如果队列已经满了,那么生产线程将会阻塞
  2. 阻塞队列的分类
  1. ArrayBlockingQueue
  1. 有界队列
  2. 底层为Array形式存储
  3. 如果所有的任务都是按顺序执行,不存在“插队”和从队伍中离开,则适合使用ArrayBlockingQueue
  1. LinkedBlockingQueue
  1. 无界队列
  2. 底层为链表形式存储
  3. 如果存在“插队”,和从队伍中离开的情况则适合使用LinkedBlockingQueue
  1. SynchronousQueue
  1. 只能有一个元素
  1. 阻塞队列的使用示例 略
  1. 使用线程的优势
  1. 减少了线程创建,销毁的性能消耗,复用了线程
  2. 充分利用cpu的内核(相对于单线程),省略了上下文的切换
  3. 提高了线程的可管理性,线程池可以对线程统一分配,调优和监控
  4. 由于减少了线程的创建时间,所以响应速度会有所提高
  1. 线程池的几个参数作用
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. 将新的任务交给调用者执行
  1. new ThreadPoolExecutor.AbortPolicy()
  1. 直接抛出RejectedExecutionException异常
  1. new ThreadPoolExecutor.DiscardPolicy()
  1. 丢弃调新的任务,但不抛出异常
  1. new ThreadPoolExecutor.DiscardOldestPolicy()
  1. 丢弃队列中最老的任务,但不抛出异常
  1. 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

  1. 带返回值的线程池
  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;
        }
    }
}
  1. ThreadPoolExecutor的execute方法和submit方法区别
  1. execute只能执行实现了Runnable接口的类;submit可以执行实现了Runnable接口的类也可以执行实现了Callable接口的类
  2. submit方法可以通过返回值Future获取到子线程的执行结果和异常信息, 如果线程正确执行完毕future.get()获取到线程的返回结果,如果子线程抛出异常future.get()获取到异常信息