三大方法

我们可以通过工具类Executors来创建线程池,一般用三个方法

方法一

  1. public static void main(String[] args) {
  2. //创建只有一个线程的线程池
  3. ExecutorService threadPool = Executors.newSingleThreadExecutor();
  4. try {
  5. for (int i = 0; i < 10; i++) {
  6. threadPool.execute(() -> {
  7. System.out.println(Thread.currentThread().getName() + " 正在执行中...");
  8. });
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. } finally {
  13. //关闭线程池
  14. threadPool.shutdown();
  15. }
  16. }

image.png
可以看到线程池只有一个线程

方法二

  1. public static void main(String[] args) {
  2. //创建固定线程数量的线程池
  3. ExecutorService threadPool = Executors.newFixedThreadPool(5);
  4. try {
  5. for (int i = 0; i < 10; i++) {
  6. threadPool.execute(() -> {
  7. System.out.println(Thread.currentThread().getName() + " 正在执行中...");
  8. });
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. } finally {
  13. //关闭线程池
  14. threadPool.shutdown();
  15. }
  16. }

image.png
方法三

  1. public static void main(String[] args) {
  2. //创建线程数量可伸缩的的线程池,不过也会有最大数量,不可能无限大
  3. ExecutorService threadPool = Executors.newCachedThreadPool();
  4. try {
  5. for (int i = 0; i < 10; i++) {
  6. threadPool.execute(() -> {
  7. System.out.println(Thread.currentThread().getName() + " 正在执行中...");
  8. });
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. } finally {
  13. //关闭线程池
  14. threadPool.shutdown();
  15. }

image.png

七大参数

再阿里巴巴开发规范中,明确规定不能直接使用工具类Executors来创建线程池,而需要其底层的TreadPoolExecutor类来创建:
image.png
TreadPoolExecutor类构造器源码:

  1. public static void main(String[] args) {
  2. public ThreadPoolExecutor(
  3. int corePoolSize, // 核心线程池大小
  4. int maximumPoolSize, // 最大线程池大小
  5. long keepAliveTime, // 超过规定时间没有人调用就会释放
  6. TimeUnit unit, // 超时单位
  7. BlockingQueue<Runnable> workQueue, // 阻塞队列
  8. ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
  9. RejectedExecutionHandler handle // 拒绝策略
  10. )
  11. {
  12. if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
  13. throw new IllegalArgumentException();
  14. if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException();
  15. this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
  16. this.corePoolSize = corePoolSize;
  17. this.maximumPoolSize = maximumPoolSize;
  18. this.workQueue = workQueue;
  19. this.keepAliveTime = unit.toNanos(keepAliveTime);
  20. this.threadFactory = threadFactory;
  21. this.handler = handler;
  22. }
  23. }

关于使用TreadPoolExecutor创建线程池,通过一个银行排队办理业务的方式介绍这个类的几个参数

image.png
人相当于需要使用该线程池执行的任务数量;
corePoolSize相当于正常开启的窗口1和窗口2;
maximumPoolSize相当于全部窗口,只有当等候席满人且还有人来办理业务的时候开启;
workQueue则相当于座位1~6,即阻塞队列的容量是6;
handle则是当所有窗口满人,等待席也满人但是依然有人想进银行办理业务时的拒绝策略;

测试类:

  1. public class ThreadPoolTest {
  2. public static void main(String[] args) {
  3. ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
  4. 5,
  5. 6,
  6. TimeUnit.SECONDS,
  7. new ArrayBlockingQueue<>(6),
  8. //Executors工具类的默认线程工厂
  9. Executors.defaultThreadFactory(),
  10. //队列满了,抛出异常!
  11. new ThreadPoolExecutor.AbortPolicy()
  12. );
  13. try {
  14. //x是未知数
  15. for (int i = 0; i < x; i++) {
  16. threadPool.execute(()->{
  17. System.out.println(Thread.currentThread().getName() + "正在执行中...");
  18. });
  19. }
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. } finally {
  23. //关闭线程池
  24. threadPool.shutdown();
  25. }
  26. }
  27. }

下面看几种情况
当x在(0,corePoolSize+workQueue.size=2+6=8]时:
image.png
这时候始终只有两条线程在执行任务(图为x=8情况)

当x在(corePoolSize+workQueue.size=2+6=8,maximumPoolSize+workQueue.size=5+6=11]:
image.png
这时候就出现3条及以上的线程开始执行任务了(图为x=11的情况)

当x在(maximumPoolSize+workQueue.size=5+6=11,m=大于11的数]:
image.png
这时候就开始执行拒绝策略了(图为x=12的情况)

四种拒绝策略

// 银行满了,还有人进来,不处理这个人的,抛出异常
new ThreadPoolExecutor.AbortPolicy() 

// 哪来的去哪里,比如:主线程调用的线程池执行的话就去主线程执行任务
new ThreadPoolExecutor.CallerRunsPolicy()

//队列满了,丢掉任务,但不会抛出异常
new ThreadPoolExecutor.DiscardPolicy() 

//队列满了,尝试去和最早执行的任务竞争,也不会抛出异常    
new ThreadPoolExecutor.DiscardOldestPolicy()