Java线程池-ThreadPoolExecutor,Executors使用示例

1. Executors简单示例

java提供了Executors线程池创建工具方便我们创建合适的线程池,示例如下,提供了四种创建线程池的简单方法,当然,其中有各自的优劣,之后 再叙述。

//创建一个会根据需要自动创建线程的线程池,并且有空闲线程存在时,不再创建新的线程,将重用该空闲线城池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 
//创建并保持固定数量的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//利用主机上可用处理器的数量作为目标并行级别,创建一个基于工作-窃取算法的线程池
ExecutorService workStealingPool = Executors.newWorkStealingPool();
//创建一个线程池,可以调度命令在指定延迟后运行或周期性执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);

接下来,根据上面java提供的简单方法创建一个CachedThreadPool简单的示例。

public class CachedTheadPoolDemo {

    public static void main(String[] args){
        // 创建线程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 创建3个线程
        for (int i = 0; i < 3; i++) {
            CustomTask customTask = new CustomTask(i + "");
            cachedThreadPool.execute(customTask);
        }
    }
}

// 自定义一个任务
class CustomTask extends Thread {

    private String taskName;

    public CustomTask(String taskName){
        this.taskName = taskName;
    }
    @Override
    public void run() {
        // 循环打印出任务名和标签
        for (int i = 0; i < 3; i++) {
            System.out.println("Task: "+taskName+", Tag: "+i);
        }
    }
}

运行结果如下,

Task: 0, Tag: 0
Task: 1, Tag: 0
Task: 2, Tag: 0
Task: 1, Tag: 1
Task: 0, Tag: 1
Task: 1, Tag: 2
Task: 2, Tag: 1
Task: 0, Tag: 2
Task: 2, Tag: 2

2. ThreadPoolExecutor简单示例

在阿里巴巴java开发规范手册中,并不推荐使用Executors工具去创建线程池,而是使用ThreadPoolExecutor创建线程池至于原因,在下面再讨论。那么,先看看ThreadPoolExecutor是怎么创建线程池的,示例如下

import java.util.concurrent.*;

public class ThreadPoolExecutorDemo {

    public static void main(String[] args){

        int corePoolSize = 4;  // 一直保持在线程池内的线程数量,这个数量内的线程就算空闲也不会被回收,除非设置了allowCoreThreadTimeOut
        int maximumPoolSize = 8; // 线程池允许持有的最大线程数量
        long keepAliveTime = 10; // 当线程数量大于核心线程数量(corePoolSize)时,回收空闲线程的最大时间
        TimeUnit unit = TimeUnit.SECONDS; // keepAliveTime的时间单位
        BlockingQueue workQueue = new LinkedBlockingDeque<>(4); // 保存被执行前任务的队列,这个队列只会保存被execute方法提交的Runnable任务
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 创建线程的工厂类
        RejectedExecutionHandler rejectedPolicy = new ThreadPoolExecutor.AbortPolicy(); // 当线程阻塞并且达到任务队列最大容量时调用的处理器

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedPolicy);
        for (int i = 0; i < 3; i++) {
            CustomTask customTask = new CustomTask(i + "");
            threadPoolExecutor.execute(customTask);
        }

    }
}

class CustomTask extends Thread {

    private String taskName;

    public CustomTask(String taskName){
        this.taskName = taskName;
    }
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("Task: "+taskName+", Tag: "+i);
        }
    }
}

控制台输出结果,

Task: 2, Tag: 0
Task: 1, Tag: 0
Task: 0, Tag: 0
Task: 1, Tag: 1
Task: 2, Tag: 1
Task: 1, Tag: 2
Task: 0, Tag: 1
Task: 2, Tag: 2
Task: 0, Tag: 2

 

3. Executors源码分析

在第1节中,以及了解到了Executors线程池创建工具的简单使用,那么这一节将从源码的角度去看待各种创建方法的不同。

3.1 CachedThreadPool

// 创建一个可以根据需要创建新线程的线程池
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                   new SynchronousQueue());
}

定义Executors调用newCachedThreadPool()方法创建一个线程池,该线程池的核心线程数为0,线程池可持有的最大线程数为Integer类型的最大值2^{31}-1,回收空闲线程的等待时间为60秒,采用阻塞队列保存任务(Task)。

 

3.2 FixedThreadPool

// 创建一个保存固定线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}

定义Executors调用newFixedThreadPool(int nThreads)方法创建一个核心线程数量和线程数量相同的线程池,采用基于linked nodes的可选非阻塞队列保存任务,队列初始容量为Integer类型的最大值2^{31}-1

3.3 WorkStealingThreadPool

//用可用处理器数量作为目标并行级别创建一个work-stealing的线程池
public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool
         (Runtime.getRuntime().availableProcessors(),
          ForkJoinPool.defaultForkJoinWorkerThreadFactory,
          null, true);
}

定义: Executors调用newWorkStealingPool()方法创建一个线程池,该线程池为ForkJoinPool线程池,继承了AbstractExecutorService,使用DefaultForkJoinWorkerThreadFactory作为默认创建新线程的工厂类,默认对内部工作线程执行任务出现不可恢复错误时不做任何处理,并为未加入的forked task建立本地先进先出的任务调度模式。

3.4 ScheduledThreadPool

// Executors中,传入核心线程数调用ScheduledThreadPool的构造函数,创建线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
   return new ScheduledThreadPoolExecutor(corePoolSize);
}




// ScheduledThreadPoolExecutor中构造函数,创建一个线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

定义: Executors调用newScheduledThreadPool方法时,会间接调用ScheduledThreadPoolExecutor的构造函数创建线程池,该线程池核心线程数为corePoolSize,可持有的最大线程数为Integer类型的最大值2^{31}-1,回收空闲线程的默认时间为10毫秒,采用一个专门的延迟队列,该队列也为阻塞队列,初始容量为16。

你可能感兴趣的:(多线程,队列,java,thread)