Java线程池及线程池工具类

Java线程池及线程池工具类

  • 线程池介绍
  • 线程池使用及参数介绍
    • 线程池使用
    • 线程池参数介绍
      • 七大核心参数
      • 等待队列
      • 拒绝策略
        • JDK中内置的拒绝策略
        • 自定义实现拒绝策略
    • 线程池执行流程
  • 线程池工具类创建线程池
    • newFixedThreadPool
    • newSingleThreadExecutor
    • newCachedThreadPool
    • newScheduledThreadPool
    • newSingleThreadScheduledExecutor
    • newWorkStealingPool
  • 自定义异常处理器

线程池介绍

启动一个新线程涉及与操作系统交互,成本比较高。线程池在程序启动时创建大量空闲线程,程序将线程对象传给线程池,线程就会启动一个线程来执行它的run()或call()方法,方法执行结束后,线程并不会死亡,而是再次返回线程池中称为空闲状态,等待下一次执行。使用线程池可以避免频繁创建和销毁线程,可以使创建的线程得到复用,同时使用线程池可以有效控制系统中并发线程的数量。

线程池使用及参数介绍

线程池使用

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(参数);

public static void main(String[] args) {
        MyTestThreadPool myTestThreadPool = new MyTestThreadPool(3,3,0L,TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(10));

        for (int i = 0; i < 100; i++) {
            Runnable target = () -> {
                for (int j = 0; j < 10; j++) {
                    System.out.println("j = " + j);
                }
            };
            myTestThreadPool.submit(target);
        }
    }

线程池全参构造源码:

    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

线程池参数介绍

七大核心参数

corePoolSize
指定线程池中核心线程数
maximumPoolSize
指定线程池中最大线程数量:核心线程数量+非核心线程数量
keepAliveTime
当线程池中线程数量超过corePoolSize时,多余线程存活的时间(非核心线程存活时间),即非核心线程在多长时间后被销毁
unit
keepAliveTime的时间单位
workQueue
任务队列,被提交到那时尚未被执行的任务
threadFactory
线程工厂,用于创建线程
handler
拒绝策略。当任务太多来不及处理时,如何拒绝任务。

等待队列

SynchronousQueue
SynchronousQueue没有容量,每一个put操作都需要等待一个对应的take操作。如果使用SynchronousQueue,则提交的任务不会被真实地保存,而是将新的任务提交给线程执行。
ArrayBlockingQueue
数组实现的有界阻塞队列,ArrayBlockingQueue的构造函数必须带参数,指定该队列的最大容量。此队列按照先进先出的顺序对元素进行排序。可以通过构造函数设定队列是否公平new ArrayBlockingQueue(int capacity, boolean fair)。默认情况下为非公平的访问队列。
LinkedBlockingQueue
LinkedBlockingQueue是基于链表实现。LinkedBlockingQueue中对生产者和消费者分别采用了独立的锁来控制数据同步,这也意味着在高并发情况下,生产者和消费者可以并行操作队列中的数据,提高整个队列的并发性能。
PriorityBlockingQueue
优先任务队列,带有执行优先级,可以控制任务执行的先后顺序。可以自定义是compareTo方法来实现排序,也可以指定构造参数Comparator来对数据进行排序。
DelayQueue
是一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue实现,队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延时期满时才能从队列中提取元素。

拒绝策略

JDK中内置的拒绝策略

当线程池中的任务数量超过负载时就会使用到拒绝策略。JDK中内置了四种拒绝策略:

  • AbortPolicy:该策略直接抛出异常,阻止线程继续工作;
  • CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中运行当前任务。这种方式可能造成任务提交线程的性能急剧下降;
  • DiscardOldestPolicy:该策略会丢弃最老的请求,也就是即将执行的一个任务,并尝试再次提交;
  • DiscardPolicy:该策略默认丢弃无法处理的任务。
自定义实现拒绝策略

自行扩展RejectedExecutionHandler接口。

线程池执行流程

简单描述: 核心线程(不满足时下一步,否则执行任务) -> 等待队列(不满足时下一步,否则执行任务) -> 非核心线程(不满足时下一步,否则执行任务) -> 拒绝策略。
具体步骤:

  • 当添加一个任务时,线程会做如下判断:
    (1)如果正在执行的线程数量小于corePoolSize,则马上创建线程执行这个任务;
    (2)如果正在执行的线程数量大于或等于corePoolSize,那么将这个任务放入等待队列;
    (3)如果队列满了,并且正在运行的线程数小于maximumPoolSize,则创建非核心线程处理该任务;
    (4)如果队列满了,并且正在运行的线程数大于或等于maximumPoolSize,则会抛出RejectedExecutionHandler(线程池设置的拒绝策略)。
  • 当一个线程完成任务时会到队列中取下一个任务来执行。
  • 当线程无事可做,超过设置等待时间,如果当前线程池线程数量大于maximumPoolSize,这个线程就会被干掉,线程池线程数量收缩到核心线程数大小。

线程池工具类创建线程池

java.util.concurrent.Executors工具类中提供了具有特定功能的线程池。

newFixedThreadPool

该方法创建一个可重用、有固定线程数量的线程池。该池中的线程数量始终不变。当有一个新任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被放入等待队列。

源码分析:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

该线程池核心线程数与最大线程数相等,使用LinkedBlockingQueue作为等待队列,该队列的默认大小为Integer.MAX_VALUE,当任务提交非常频繁时,可能导致线程大量堆积。

newSingleThreadExecutor

该方法创建一个只有单线程的线程池,若提交任务时线程被詹总,则任务进入等待队列,待线程空闲,按照先进先出的顺序执行队列中的任务。
源码分析:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

该线程池核心线程池与最大线程池数均为1,使用LinkedBlockingQueue作为等待队列,该队列的默认大小为Integer.MAX_VALUE,可能导致线程大量堆积。

newCachedThreadPool

该方法创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程会被缓存在线程池中。线程池中线程数量不确定,但若有空闲线程可以复用,会优先使用可复用的线程。若所有线程均在工作,则会创建新的线程处理任务。所有线程在当前线程执行完毕后,将返回线程池进行复用。
源码分析:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

该线程池核心线程数为0,最大线程数为Integer.MAX_VALUE,使用了SynchronousQueue等待队列,该队列不存储任务,放入一个任务必须等待一个线程来处理任务。空闲线程会在60秒后被回收。使用该线程池,若同时有大量任务提交,而任务执行并不快,那么系统会创建大量线程来处理任务,这样做可能会很快耗尽系统资源。

newScheduledThreadPool

该方法创建具有指定线程数量的线程池,它可以在指定延迟后执行线程任务或周期性执行某个任务。该方法返回一个ScheduledExecutorService对象。
源码分析:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

newSingleThreadScheduledExecutor

该方法返回ScheduledExecutorService对象,线程池的大小为1。
源码分析:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

newWorkStealingPool

该方法床架持有足够线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争。
源码分析:

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

自定义异常处理器

1、自定义线程池时重写protected void afterExecute(Runnable r, Throwable t)方法,对异常进行处理;
2、定义自己的异常处理器,通过Thread的setUncaughtExceptionHandler(new MyExHandler());方法,设置异常处理器。

class MyExHandler implements  Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("线程" + t + "出现了异常:" + e);
    }
}

你可能感兴趣的:(#,并发编程,java,高并发,多线程)