首页 🔴 Java

一般在创建线程池的时候会使用工具类(Executors)来创建线程池, 其中有几个常见的创建方式

// 创建只有一个线程的线程池
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
// 创建一个固定大小的线程池
ExecutorService executorService2 = Executors.newFixedThreadPool(5);
// 创建一个可以自由伸缩的线程池
ExecutorService executorService3 = Executors.newCachedThreadPool();

但是通过阅读源码我发现, 这样创建线程池会有弊端

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

这里的 Integer.MAX_VALUE 是一个较大的数字, 当然如果在自己的电脑上创建这么多个线程, 肯定会报OOM的, 所以这样创建线程池是不安全的, 而且也看不到底层的实现原理, 再一, 阿里巴巴Java开发手册上也写了, 不建议这种创建线程池的方式
ali.png
再次查看源码也可以发现, 使用 Executors 来创建线程也是调用的 ThreadPoolExecutor , 那么我们也可以用这种原生的方式去创建线程

 public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
......
}

这里一共包含了七个参数, 解释如下:

  • corePoolSize 核心线程数
  • maximumPoolSize 最大线程数(最大线程数-核心线程数=救急线程数)
  • keepAliveTime 存活时间(救急线程的存活时间)
  • TimeUnit 时间单位
  • workQueue 任务队列
  • threadFactory 线程工厂
  • handler 拒绝策略

ps: 官方一共提供了四种拒绝策略如下:

  • AbortPolicy() 抛出异常
  • DiscardPolicy() 悄悄扔掉(不执行也不抛异常)
  • CallerRunsPolicy() 哪来的回哪去(交给调用线程池的线程执行)
  • DiscardOldestPolicy() 扔掉等待队列中最早的任务

创建线程池的例子:

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}

ps: 这里将核心数设为了2, 最大数设为了3, 阻塞队列最大值为3

场景一: 当待执行任务数$\leq$2个时只会出发两个线程

for (int i = 1; i <= 2; i++) {
    threadPoolExecutor.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}
threadPoolExecutor.shutdown();

控制台输出
xc1.png
场景二: 当待执行任务数$>$2并且$\leq$5个时也只会出发两个线程, 但是多余的任务会进入阻塞队列, 待之前的任务执行完毕在执行

for (int i = 1; i <= 5; i++) {
    threadPoolExecutor.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}
threadPoolExecutor.shutdown();

控制台输出
xc2.png
场景三: 当代执行任务数$>$5并且$\leq$8个时会依次触发剩下的三个线程

for (int i = 1; i <= 8; i++) {
    threadPoolExecutor.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}
threadPoolExecutor.shutdown();

控制台输出
xc3.png
场景四: 当代执行任务数$>$8个时会触发拒绝策略(我这里用的是抛出异常)

for (int i = 1; i <= 9; i++) {
    threadPoolExecutor.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}
threadPoolExecutor.shutdown();

控制台输出
xc4.png




文章评论