创建新线程,需要和操作系统进行交互,成本比较高。

一、基础概念

1657422352625.png
KeepAliveTime:是多余的线程等待新任务的最长时间,超过这个时间多余的线程将被终止
workQueue:任务存储队列。

  1. 3种常见的队列:
  2. 1. 直接交接 SynchronousQueueSynchronousQueuecapacity0,即不存储任何元素。
  3. 2. 无界队列 LinkedBlockingQueue
  4. 3. 无界队列 ArrayBlockingQueue

threadFactory: 指定创建线程的工厂,一般不用指定,用默认的defaultThreadFactory即可。

二、几种线程池结构

newFixedThreadPool

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

即核心线程数和最大线程数保持一致,因为是固定线程数,所以即使是线程空闲状态也不进行回收,所以KeepAliveTime也为0,任务队列采用LinkedBlockingQueue无界队列,因为当线程数超过最大线程数以后,需要将所有新增的线程放到无界队列里。
OOM异常原因:线程不断新增并且阻塞,导致无界队列占用内存超过锁分配的内存。
实现:

  1. public class FixedThreadPool {
  2. public static void main(String[] args) {
  3. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
  4. for (int i = 0; i < 100; i++) {
  5. threadPoolExecutor.execute(new MyRunnable());
  6. }
  7. }
  8. static class MyRunnable implements Runnable {
  9. @Override
  10. public void run() {
  11. String name = Thread.currentThread().getName();
  12. System.out.println("当前线程名:" + name);
  13. }
  14. }
  15. }

newCachedThreadPool

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

缓存线程池的核心线程数为0,最大线程数Integer.MAX_VALUE,即可以理解可以不断创建新的线程,任务队列使用SynchronousQueue,SynchronousQueue的容量为0,即不存线程.
OOM原因:每个线程任务都要新增一个线程,每个线程都要占用内存资源,一定数量导致OOM。

ScheduledThreadPoolExecutor

  1. public ScheduledThreadPoolExecutor(int corePoolSize) {
  2. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
  3. new DelayedWorkQueue());
  4. }

ScheduledThreadPoolExecutor主要用来在给定的延迟之后运 行任务,或者定期执行任务。由于队列使用的是DelayQueue是一个无界队列,所以这里的最大线程数maximumPoolSize没有什么意义。
实现:

  1. public class ScheduledThreadPoolTest {
  2. public static void main(String[] args) {
  3. System.out.println("开始执行!");
  4. ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
  5. //1.延迟5秒钟执行
  6. //scheduledExecutorService.schedule(new MyRunnable(),5, TimeUnit.SECONDS);
  7. //2.初始延迟3秒钟开始执行,每5秒钟执行一次
  8. scheduledExecutorService.scheduleAtFixedRate(new MyRunnable(),3,5,TimeUnit.SECONDS);
  9. }
  10. static class MyRunnable implements Runnable {
  11. @Override
  12. public void run() {
  13. String name = Thread.currentThread().getName();
  14. System.out.println("当前线程名:" + name);
  15. }
  16. }
  17. }

三、停止线程池的方法

  1. void shutdown();
  2. boolean isShutdown();
  3. List<Runnable> shutdownNow();
  4. boolean isTerminated();
  5. boolean awaitTermination(long timeout, TimeUnit unit);

shutdown()

  1. public class ShutDownTest {
  2. public static void main(String[] args) {
  3. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
  4. for (int i = 0; i < 1000; i++) {
  5. threadPoolExecutor.execute(new MyTask());
  6. }
  7. threadPoolExecutor.shutdown();
  8. threadPoolExecutor.execute(new MyTask());
  9. }
  10. static class MyTask implements Runnable{
  11. @Override
  12. public void run() {
  13. try {
  14. Thread.sleep(5000);
  15. System.out.println(Thread.currentThread().getName());
  16. } catch (InterruptedException e) {
  17. System.out.println(Thread.currentThread().getName() + "被中断了");
  18. }
  19. }
  20. }
  21. }

image.png
当执行了shutdown方法,1000个线程有995个在队列里,队列里的任务仍然会执行,只是不能再执行新的线程。

isShutdown()

  1. //当执行了shutdown方法以后,可以通过该方法判断线程池是否停止。
  2. executorService.isShutdown()

isTerminated()

  1. boolean flag=executorService.isTerminated()

isTerminated()isShutdown()方法的区别在于:isShutdown()只要执行了shutdown()方法,就会返回true,而isTerminated()方法需要等延迟队列里所有的任务执行完成才会返回true。

awaitTermination()

  1. boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);

上述代码表示7秒钟之内,判断线程池是否仍有未执行完成的线程。

shutdownNow()

  1. public class ShutdownNowTest {
  2. public static void main(String[] args) throws InterruptedException {
  3. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
  4. for (int i = 0; i < 1000; i++) {
  5. threadPoolExecutor.execute(new MyTask());
  6. }
  7. Thread.sleep(1500);
  8. List<Runnable> runnables = threadPoolExecutor.shutdownNow();
  9. }
  10. static class MyTask implements Runnable{
  11. @Override
  12. public void run() {
  13. try {
  14. Thread.sleep(500);
  15. System.out.println(Thread.currentThread().getName());
  16. } catch (InterruptedException e) {
  17. System.out.println(Thread.currentThread().getName() + "被中断了");
  18. }
  19. }
  20. }
  21. }
  22. //结果如下:
  23. pool-1-thread-5
  24. pool-1-thread-3
  25. pool-1-thread-4
  26. pool-1-thread-2
  27. pool-1-thread-5
  28. pool-1-thread-1
  29. pool-1-thread-3
  30. pool-1-thread-4被中断了
  31. pool-1-thread-5被中断了
  32. pool-1-thread-1被中断了
  33. pool-1-thread-3被中断了
  34. pool-1-thread-2被中断

可以看到,正在执行的核心线程立即都被中断了,同时,该方法返回队列里所有未执行的线程,这样可以将未执行的进行日志记录等等。