线程池基础知识

2023-10-27

1、为什么要使用线程池,线程池用什么用

  创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。(我们可以把创建和销毁的线程的过程去掉)

  1. 降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
  2. 提高响应速度:任务到达时不需要等待线程创建就可以立即执行
  3. 提高线程的可管理性:线程池可以统一管理、分配、调优和监控

2、几种常见的线程池及使用场景

JAVA通过Executors提供了四种线程池:

  单线程化线程池(newSingleThreadExecutor)

  可控最大并发数线程池(newFixedThreadPool)

  可回收缓存线程池(newCachedThreadPool)

  支持定时与周期性任务的线程池(newScheduledThreadPool)

(1)newSingleThreadExecutor

线程池特点:

  • 核心线程数和最大线程数大小一样且都是1
  • keepAliveTime为0
  • 阻塞队列是LinkedBlockingQueue

该线程池的工作机制是:

  1. 线程池中没有线程时,新建一个线程执行任务
  2. 有一个线程以后,将任务加入阻塞队列,不停加加加
  3. 唯一的这一个线程不停地去队列里取任务执行

适用场景:

SingleThreadExecutor适用于串行执行任务的场景,每个任务必须按顺序执行,不需要并发执行。

 

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

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

单线程化线程池(newSingleThreadExecutor)的优点,串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

使用单线程化线程池(newSingleThreadExecutor)的一般方法下代码所示。

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 import java.util.concurrent.TimeUnit;
 4  
 5 public class ThreadPoolByNewSingleThreadExecutor {
 6  
 7     public static void main(String[] args) {
 8         /**
 9          * 单线程化的线程池
10          */
11         ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
12         for (int i = 0; i < 10; i++) {
13             final int index = i;
14             singleThreadExecutor.execute(new Runnable() {
15                 @Override
16                 public void run() {
17                     Thread.currentThread().setName("Thread i = " + index);
18                     System.out.println(Thread.currentThread().getName() + " index = " + index);
19                     try {
20                         Thread.sleep(500);
21                     } catch (InterruptedException e) {
22                         System.out.println("ssss");
23                     }
24                 }
25             });
26         }
27         singleThreadExecutor.shutdown();
28         System.out.println("on the main thread...");
29         
30     }
31  
32 }

 

参考:https://blog.csdn.net/android2011_1/article/details/79629890

(2)newFixedThreadPool

线程池特点:

  • 核心线程数和最大线程数大小一样
  • keepAliveTime为0
  • 阻塞队列是LinkedBlockingQueue

它是固定大小的线程池,其核心线程数和最大线程数大小一样。并且阻塞队列用的是LinkedBlockingQueue,也就是说线程最大数这个参数失效了基本,所以不会出现外包线程的存在,所以也可以认为keepAliveTime参数是一个摆设。除非allowCoreThreadTimeOut方法的调用。

该线程池的工作机制是:

  1. 线程数少于核心线程数,也就是设置的线程数时,新建线程执行任务
  2. 线程数等于核心线程数后,将任务加入阻塞队列
    1. 由于队列容量非常大(Integer.MAX_VALUE),可以一直加加加。(当线程池中的任务比较特殊时,比如关于数据库的长时间的IO操作,可能导致OOM)
  3. 执行完任务的线程反复去队列中取任务执行

适用场景:

FixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程即可。一般Ncpu+1

 

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

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

刚刚介绍了单线程化线程池newSingleThreadExecutor,可控最大并发数线程池(newFixedThreadPool)与其最大的区别是可以通知执行多个线程,可以简单的将newSingleThreadExecutor理解为newFixedThreadPool(1)。例如运行一下两个程序:
单线程化线程池(newSingleThreadExecutor)示例:

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3  
 4 public class ThreadPoolByNewSingleThreadExecutor {
 5  
 6     public static void main(String[] args) {
 7         /**
 8          * 单线程化的线程池
 9          */
10         ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
11         for (int i = 0; i < 10; i++) {
12             final int index = i;
13             singleThreadExecutor.execute(new Runnable() {
14                 @Override
15                 public void run() {
16                     Thread.currentThread().setName("Thread i = " + index);
17                     System.out.println(Thread.currentThread().getName() + " index = " + index);
18                     try {
19                         Thread.sleep(500);
20                     } catch (InterruptedException e) {
21                         System.out.println("exception");
22                     }
23                 }
24             });
25         }
26         singleThreadExecutor.shutdown();
27         System.out.println("on the main thread...");
28         
29     }
30  
31 }

可控最大并发数线程池(newFixedThreadPool)示例:

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3  
 4 public class ThreadPoolByNewFixedThreadPool {
 5     public static void main(String[] args) {
 6         
 7         ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
 8         for (int i = 0; i < 10; i++) {
 9             final int index = i;
10             newFixedThreadPool.execute(new Runnable() {
11                 @Override
12                 public void run() {
13                     Thread.currentThread().setName("Thread i = " + index);
14                     System.out.println(Thread.currentThread().getName() + " index = " + index);
15                     try {
16                         Thread.sleep(500);
17                     } catch (InterruptedException e) {
18                         System.out.println("exception");
19                     }
20                 }
21             });
22         }
23         newFixedThreadPool.shutdown();
24         System.out.println("on the main thread...");
25     }
26  
27 }

结果从显示上看虽然很相似,但是观察到的执行效果确实完全不一致的,newSingleThreadPool中,只有一个线程,每次输出一行后暂停0.5秒,newFixedThreadPool(3)中可以创建3个线程,一次输出3行后暂停0.5秒(当然是这三个线程都暂停0.5秒)。

动画对比如下所示:

 

(3)newCachedThreadPool

线程池特点:

  • 核心线程数为0,且最大线程数为Integer.MAX_VALUE
  • 阻塞队列是SynchronousQueue

SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue

锁当提交任务的速度大于处理任务的速度时,每次提交一个任务,就必然会创建一个线程。极端情况下会创建过多的线程,耗尽 CPU 和内存资源。由于空闲 60 秒的线程会被终止,长时间保持空闲的 CachedThreadPool 不会占用任何资源。

该线程池的工作机制是:

  1. 没有核心线程,直接向SynchronousQueue中提交任务
  2. 如果有空闲线程,就去取出任务执行;如果没有空闲线程,就新建一个
  3. 执行完任务的线程有60秒生存时间,如果在这个时间内可以接到新任务,就可以继续活下去,否则就拜拜

适用场景:

CachedThreadPool 用于并发执行大量短期的小任务。

 

 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

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

  以上介绍了单线程化线程池(newSingleThreadExecutor)、可控最大并发数线程池(newFixedThreadPool)。下面介绍的是第三种newCachedThreadPool——可回收缓存线程池。

  在JAVA文档中是这样介绍可回收缓存线程池的:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟(默认)未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。(可以通过java.util.concurrent.ThreadPoolExecutor类的构造方法构造更加具体的类,例如指定时间参数)

        创建线程本身需要很多资源,包括内存,记录线程状态,以及控制阻塞等等。因此,相比另外两种线程池,在需要频繁创建短期异步线程的场景下,newCachedThreadPool能够复用已完成而未关闭的线程来提高程序性能。

newCachedThreadPool使用方法与其他两种线程池使用方法类似,基本使用代码如下所示:

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3  
 4 public class NewCachedThreadPoolExecutor {
 5  
 6     public static void main(String[] args) {
 7         ExecutorService cachedThreadExecutor = Executors.newCachedThreadPool();
 8         for (int i = 0; i < 10; i++) {
 9             final int index = i;
10             try {
11                 Thread.sleep(1000);
12             } catch (InterruptedException e1) {
13                 e1.printStackTrace();
14             }
15             cachedThreadExecutor.execute(new Runnable() {
16                 @Override
17                 public void run() {
18                     System.out.println(Thread.currentThread().getName() + ": index = " + index);
19                 }
20             });
21         }
22         cachedThreadExecutor.shutdown();
23         
24     }
25  
26 }

执行结果如下图,会发现线程的名字都一样,因为在执行后续任务的时候,上一个任务已经完成,会复用上一个任务的线程资源来执行。

 

(4)newScheduledThreadPool

线程池特点:

  • 最大线程数为Integer.MAX_VALUE
  • 阻塞队列是DelayedWorkQueue

ScheduledThreadPoolExecutor 添加任务提供了另外两个方法:

  • scheduleAtFixedRate() :按某种速率周期执行
  • scheduleWithFixedDelay():在某个延迟后执行

两种方法的内部实现都是创建了一个ScheduledFutureTask对象封装了任务的延迟执行时间及执行周期,并调用decorateTask()方法转成RunnableScheduledFuture对象,然后添加到延迟队列中。

DelayQueue:中封装了一个优先级队列,这个队列会对队列中的ScheduledFutureTask 进行排序,两个任务的执行 time 不同时,time 小的先执行;否则比较添加到队列中的ScheduledFutureTask的顺序号 sequenceNumber ,先提交的先执行。

该线程池的工作机制是:

  1. 调用上面两个方法添加一个任务
  2. 线程池中的线程从 DelayQueue 中取任务
  3. 然后执行任务

具体执行步骤:

  1. 线程从 DelayQueue 中获取 time 大于等于当前时间的 ScheduledFutureTask
    1. DelayQueue.take()
  2. 执行完后修改这个 task 的 time 为下次被执行的时间
  3. 然后再把这个 task 放回队列中
    1. DelayQueue.add()

适用场景:

ScheduledThreadPoolExecutor用于需要多个后台线程执行周期任务,同时需要限制线程数量的场景。

 

创建一个定长线程池,支持定时及周期性任务执行。

1 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
2         return new ScheduledThreadPoolExecutor(corePoolSize);
3     }

newScheduledThreadPool用于构造安排线程池,能够根据需要安排在给定延迟后运行命令或者定期地执行。

在JAVA文档的介绍:

  1. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
  2. 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
  3. 参数:corePoolSize - 池中所保存的线程数,即使线程是空闲的也包括在内。
  4. 返回:新创建的安排线程池

需要注意的是,参数corePoolSize在这个方法中是没用意义的,详解见JAVA进阶----ThreadPoolExecutor机制

具体实现:

 1 import java.util.concurrent.Executors;
 2 import java.util.concurrent.ScheduledExecutorService;
 3 import java.util.concurrent.TimeUnit;
 4  
 5 public class ThreadPoolByNewScheduledThreadPool {
 6  
 7     public static void main(String[] args) {
 8         
 9         Thread thread = new Thread(new Runnable() {
10             @Override
11             public void run() {
12                 System.out.println(Thread.currentThread().getName() + " : 延迟3秒");
13             }
14         });
15         
16         /**
17          * 定长线程池,支持定时及周期性任务执行
18          */
19         ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
20         
21         //延迟3s后运行
22         scheduledThreadPool.schedule(thread, 3, TimeUnit.SECONDS);
23         
24         //首次执行延迟1s,每次间隔3秒
25         //scheduledThreadPool.scheduleAtFixedRate(thread, 1, 3, TimeUnit.SECONDS);
26         
27         //每次执行结束,已固定时延开启下次执行
28         //scheduledThreadPool.scheduleWithFixedDelay(thread, 1, 3, TimeUnit.SECONDS);
29         
30         System.out.println(Thread.currentThread().getName() + " : main thread");
31         scheduledThreadPool.shutdown();
32 //        try {
33 //            Thread.sleep(12000);
34 //        } catch (InterruptedException e) {
35 //            e.printStackTrace();
36 //        }
37 //        scheduledThreadPool.shutdownNow();
38     }
39  
40 }

后面注释掉的内容表示强制程序最大执行实际为12s,这通常是不切实际的,常常会需要在线程中设置标志位或标记系统时间来获取程序的终止时间。这就涉及到获取线程返回值的问题。将在后续文章中进行介绍。

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

3、线程池都有哪几种工作队列

(1)ArrayBlockingQueue
是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
(2)LinkedBlockingQueue
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
(3)SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
(4)PriorityBlockingQueue
一个具有优先级的无限阻塞队列

4、线程池中的几种重要的参数及流程说明

 1 public ThreadPoolExecutor(int corePoolSize,  
 2                               int maximumPoolSize,  
 3                               long keepAliveTime,  
 4                               TimeUnit unit,  
 5                               BlockingQueue<Runnable> workQueue,  
 6                               ThreadFactory threadFactory,  
 7                               RejectedExecutionHandler handler) {  
 8         if (corePoolSize < 0 ||  
 9             maximumPoolSize <= 0 ||  
10             maximumPoolSize < corePoolSize ||  
11             keepAliveTime < 0)  
12             throw new IllegalArgumentException();  
13         if (workQueue == null || threadFactory == null || handler == null)  
14             throw new NullPointerException();  
15         this.corePoolSize = corePoolSize;  
16         this.maximumPoolSize = maximumPoolSize;  
17         this.workQueue = workQueue;  
18         this.keepAliveTime = unit.toNanos(keepAliveTime);  
19         this.threadFactory = threadFactory;  
20         this.handler = handler;  
21     } 

构造方法参数讲解

参数名 作用
corePoolSize 核心线程池大小
maximumPoolSize 最大线程池大小
keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
TimeUnit keepAliveTime时间单位
workQueue 阻塞任务队列
threadFactory 新建线程工厂
RejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

 

 

 

 

 

 

重点讲解:
其中比较容易让人误解的是:corePoolSize,maximumPoolSize,workQueue之间关系。
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

1 TimeUnit.DAYS;               //
2 TimeUnit.HOURS;             //小时
3 TimeUnit.MINUTES;           //分钟
4 TimeUnit.SECONDS;           //
5 TimeUnit.MILLISECONDS;      //毫秒
6 TimeUnit.MICROSECONDS;      //微妙
7 TimeUnit.NANOSECONDS;       //纳秒

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

1 ArrayBlockingQueue
2 LinkedBlockingQueue
3 SynchronousQueue
4 PriorityBlockingQueue

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和SynchronousQueue。线程池的排队策略与BlockingQueue有关。

threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等

handler:表示当拒绝处理任务时的策略,有以下四种取值:

  1 1、AbortPolicy:直接抛出异常。
  2 2、CallerRunsPolicy:只用调用者所在线程来运行任务。
  3 3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  4 4、DiscardPolicy:不处理,丢弃掉。
  5 5、也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
  6    /**
  7      * A handler for rejected tasks that runs the rejected task
  8      * directly in the calling thread of the {@code execute} method,
  9      * unless the executor has been shut down, in which case the task
 10      * is discarded.
 11      */
 12     public static class CallerRunsPolicy implements RejectedExecutionHandler {
 13         /**
 14          * Creates a {@code CallerRunsPolicy}.
 15          */
 16         public CallerRunsPolicy() { }
 17 
 18         /**
 19          * Executes task r in the caller's thread, unless the executor
 20          * has been shut down, in which case the task is discarded.
 21          *
 22          * @param r the runnable task requested to be executed
 23          * @param e the executor attempting to execute this task
 24          */
 25         public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
 26             if (!e.isShutdown()) {
 27                 r.run();
 28             }
 29         }
 30     }
 31 
 32     /**
 33      * A handler for rejected tasks that throws a
 34      * {@code RejectedExecutionException}.
 35      */
 36     public static class AbortPolicy implements RejectedExecutionHandler {
 37         /**
 38          * Creates an {@code AbortPolicy}.
 39          */
 40         public AbortPolicy() { }
 41 
 42         /**
 43          * Always throws RejectedExecutionException.
 44          *
 45          * @param r the runnable task requested to be executed
 46          * @param e the executor attempting to execute this task
 47          * @throws RejectedExecutionException always
 48          */
 49         public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
 50             throw new RejectedExecutionException("Task " + r.toString() +
 51                                                  " rejected from " +
 52                                                  e.toString());
 53         }
 54     }
 55 
 56     /**
 57      * A handler for rejected tasks that silently discards the
 58      * rejected task.
 59      */
 60     public static class DiscardPolicy implements RejectedExecutionHandler {
 61         /**
 62          * Creates a {@code DiscardPolicy}.
 63          */
 64         public DiscardPolicy() { }
 65 
 66         /**
 67          * Does nothing, which has the effect of discarding task r.
 68          *
 69          * @param r the runnable task requested to be executed
 70          * @param e the executor attempting to execute this task
 71          */
 72         public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
 73         }
 74     }
 75 
 76     /**
 77      * A handler for rejected tasks that discards the oldest unhandled
 78      * request and then retries {@code execute}, unless the executor
 79      * is shut down, in which case the task is discarded.
 80      */
 81     public static class DiscardOldestPolicy implements RejectedExecutionHandler {
 82         /**
 83          * Creates a {@code DiscardOldestPolicy} for the given executor.
 84          */
 85         public DiscardOldestPolicy() { }
 86 
 87         /**
 88          * Obtains and ignores the next task that the executor
 89          * would otherwise execute, if one is immediately available,
 90          * and then retries execution of task r, unless the executor
 91          * is shut down, in which case task r is instead discarded.
 92          *
 93          * @param r the runnable task requested to be executed
 94          * @param e the executor attempting to execute this task
 95          */
 96         public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
 97             if (!e.isShutdown()) {
 98                 e.getQueue().poll();
 99                 e.execute(r);
100             }
101         }
102     }

ThreadPoolExecutor 源码理解 https://www.cnblogs.com/dolphin0520/p/3932921.html

 1  public static void test(int size) {
 2         ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 20, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
 3 
 4         for (int i = 0; i < size; i++) {
 5             poolExecutor.execute(new DemoTask(i));
 6 
 7 
 8             Console.log("poolSize:" + poolExecutor.getPoolSize());
 9             Console.log("corePoolSize:" + poolExecutor.getCorePoolSize());
10             Console.log("maximumPoolSize:" + poolExecutor.getMaximumPoolSize());
11             Console.log("queue:" + poolExecutor.getQueue().size());
12             Console.log("completedTaskCount:" + poolExecutor.getCompletedTaskCount());
13             Console.log("largestPoolSize:" + poolExecutor.getLargestPoolSize());
14             Console.log("keepAliveTime:" + poolExecutor.getKeepAliveTime(TimeUnit.SECONDS));
15 
16         }
17 
18         poolExecutor.shutdown();
19     }
20 
21 class DemoTask implements Runnable {
22 
23     private int taskNum;
24 
25     public DemoTask(int taskNum) {
26         this.taskNum = taskNum;
27     }
28 
29     @Override
30     public void run() {
31         Console.log(StringUtils.center("正在执行" + taskNum, 20, "="));
32 
33         try {
34             Thread.sleep(2000);
35         } catch (InterruptedException e) {
36             e.printStackTrace();
37         }
38         Console.log(StringUtils.center("执行完毕" + taskNum, 20, "="));
39     }
40 }
  1 =======正在执行0========
  2 poolSize:1
  3 corePoolSize:5
  4 maximumPoolSize:20
  5 queue:0
  6 completedTaskCount:0
  7 largestPoolSize:1
  8 keepAliveTime:2
  9 poolSize:2
 10 corePoolSize:5
 11 maximumPoolSize:20
 12 queue:0
 13 completedTaskCount:0
 14 =======正在执行1========
 15 largestPoolSize:2
 16 keepAliveTime:2
 17 poolSize:3
 18 corePoolSize:5
 19 maximumPoolSize:20
 20 =======正在执行2========
 21 queue:0
 22 completedTaskCount:0
 23 largestPoolSize:3
 24 keepAliveTime:2
 25 poolSize:4
 26 corePoolSize:5
 27 maximumPoolSize:20
 28 queue:0
 29 =======正在执行3========
 30 completedTaskCount:0
 31 largestPoolSize:4
 32 keepAliveTime:2
 33 poolSize:5
 34 corePoolSize:5
 35 =======正在执行4========
 36 maximumPoolSize:20
 37 queue:0
 38 completedTaskCount:0
 39 largestPoolSize:5
 40 keepAliveTime:2
 41 poolSize:5
 42 corePoolSize:5
 43 maximumPoolSize:20
 44 queue:1
 45 completedTaskCount:0
 46 largestPoolSize:5
 47 keepAliveTime:2
 48 poolSize:5
 49 corePoolSize:5
 50 maximumPoolSize:20
 51 queue:2
 52 completedTaskCount:0
 53 largestPoolSize:5
 54 keepAliveTime:2
 55 poolSize:5
 56 corePoolSize:5
 57 maximumPoolSize:20
 58 queue:3
 59 completedTaskCount:0
 60 largestPoolSize:5
 61 keepAliveTime:2
 62 poolSize:5
 63 corePoolSize:5
 64 maximumPoolSize:20
 65 queue:4
 66 completedTaskCount:0
 67 largestPoolSize:5
 68 keepAliveTime:2
 69 poolSize:5
 70 corePoolSize:5
 71 maximumPoolSize:20
 72 queue:5
 73 completedTaskCount:0
 74 largestPoolSize:5
 75 keepAliveTime:2
 76 poolSize:6
 77 corePoolSize:5
 78 maximumPoolSize:20
 79 queue:5
 80 completedTaskCount:0
 81 largestPoolSize:6
 82 keepAliveTime:2
 83 poolSize:7
 84 corePoolSize:5
 85 maximumPoolSize:20
 86 queue:5
 87 completedTaskCount:0
 88 largestPoolSize:7
 89 keepAliveTime:2
 90 =======正在执行11=======
 91 poolSize:8
 92 corePoolSize:5
 93 maximumPoolSize:20
 94 queue:5
 95 completedTaskCount:0
 96 =======正在执行12=======
 97 =======正在执行10=======
 98 largestPoolSize:8
 99 keepAliveTime:2
100 poolSize:9
101 corePoolSize:5
102 =======正在执行13=======
103 maximumPoolSize:20
104 queue:5
105 completedTaskCount:0
106 largestPoolSize:9
107 keepAliveTime:2
108 poolSize:10
109 corePoolSize:5
110 maximumPoolSize:20
111 =======正在执行14=======
112 queue:5
113 completedTaskCount:0
114 largestPoolSize:10
115 keepAliveTime:2
116 poolSize:11
117 corePoolSize:5
118 maximumPoolSize:20
119 queue:5
120 =======正在执行15=======
121 completedTaskCount:0
122 largestPoolSize:11
123 keepAliveTime:2
124 poolSize:12
125 corePoolSize:5
126 maximumPoolSize:20
127 queue:5
128 completedTaskCount:0
129 =======正在执行16=======
130 largestPoolSize:12
131 keepAliveTime:2
132 poolSize:13
133 corePoolSize:5
134 maximumPoolSize:20
135 =======正在执行17=======
136 queue:5
137 completedTaskCount:0
138 largestPoolSize:13
139 keepAliveTime:2
140 poolSize:14
141 corePoolSize:5
142 maximumPoolSize:20
143 queue:5
144 =======正在执行18=======
145 completedTaskCount:0
146 largestPoolSize:14
147 keepAliveTime:2
148 poolSize:15
149 corePoolSize:5
150 maximumPoolSize:20
151 =======正在执行19=======
152 queue:5
153 completedTaskCount:0
154 largestPoolSize:15
155 keepAliveTime:2
156 =======执行完毕0========
157 =======正在执行5========
158 =======执行完毕1========
159 =======执行完毕2========
160 =======正在执行6========
161 =======正在执行7========
162 =======执行完毕4========
163 =======正在执行8========
164 =======执行完毕3========
165 =======正在执行9========
166 =======执行完毕13=======
167 =======执行完毕12=======
168 =======执行完毕10=======
169 =======执行完毕11=======
170 =======执行完毕15=======
171 =======执行完毕16=======
172 =======执行完毕14=======
173 =======执行完毕19=======
174 =======执行完毕18=======
175 =======执行完毕17=======
176 =======执行完毕5========
177 =======执行完毕7========
178 =======执行完毕6========
179 =======执行完毕8========
180 =======执行完毕9========

5、线程池有哪几种工作队列

  • ArrayBlockingQueue (有界队列):是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  • LinkedBlockingQueue (无界队列):一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  • SynchronousQueue(同步队列): 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  • DelayQueue(延迟队列):一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。
  • PriorityBlockingQueue(优先级队列): 一个具有优先级得无限阻塞队列。

6、怎么理解无界队列和有界队列

  • 有界队列即长度有限,满了以后ArrayBlockingQueue会插入阻塞。
  • 无界队列就是里面能放无数的东西而不会因为队列长度限制被阻塞,但是可能会出现OOM异常。

有界队列
1.初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行 。
2.当提交的任务数超过了corePoolSize,会将当前的runable提交到一个block queue中。
3.有界队列满了之后,如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务。
4.如果3中也无法处理了,就会走到第四步执行reject操作。
无界队列
与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加,若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略。

7、如何合理配置线程池的大小

http://ifeve.com/how-to-calculate-threadpool-size/

 

 

 

参考:

Java并发:https://www.javazhiyin.com/topic/thread/page/2

https://www.iteye.com/blog/825635381-2184680

https://www.jianshu.com/p/6c6f396fc88e

https://www.jianshu.com/p/9710b899e749

转载于:https://www.cnblogs.com/116970u/p/11629079.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程池基础知识 的相关文章

  • Error creating bean with name ‘userService‘: Unsatisfied dependency expressed through field‘userMap‘

    今天在做项目时遇到了idea报 Could not autowire No beans of UserMapper type found 的错误 记录以下解决过程 我的mapper注解使用的是 Mapper 在使用 Autowrited注解
  • Java校招面试经验汇总,看完直接跪服

    美团技术一面20分钟 晚7点 因为想到下周一才面试 我刚准备出去打个羽毛球 北京的电话就来了 面试官各种抱歉 说开会拖延了 1 自我介绍 说了很多遍了 很流畅捡重点介绍完 2 问我数据结构算法好不好 挺好的 其实心还是有点虚 不过最近刷了很
  • Android 录音功能无法正常使用

    具体现象 最近在做一个APP需要安装在智能手表上 有一个录音的功能 将开发的apk安装在手机和pad上面都能正常使用 录音文件也可以正常播放 但是安装到手表后 启动录音功能几秒钟录音文件就达几百兆 且录音文件不能够正常播放 通过调试信息也看
  • Python学习笔记(十九)————json相关

    目录 1 什么是json 2 json有什么用 3 json格式数据转化 4 Python数据和Json数据的相互转化 1 什么是json JSON 是一种轻量级的数据交互格式 可以按照 JSON 指定的格式去组织和封装数据 JSON 本质
  • Grafana 系列-统一展示-2-Prometheus 数据源

    系列文章 Grafana 系列文章 Grafana Prometheus 数据源 Grafana 提供了对 Prometheus 的内置支持 本文会介绍 Grafana Prometheus 也包括 Prometheus 的兼容实现 如 T
  • js保留有效数字

    JavaScript中数字保留有效数字位数的方法有三种 toFixed toExponential toPrecision 这三种方法都会把数字转成字符串 1 toFixed 根据小数点后的指定位数将数字转为字符串 不会使用指数计数法 2
  • 使用Vue手动封装树形控件组件

    提示 如果当下认为一件事很难做 那么不用犹豫直接做就OK 巨大的提升往往出现在反复的挣扎过后 跳出舒适圈 不断挑战自我 毕竟还年轻 因为已经完成了 索性就用上帝视角一次性把需要注意的东西都列出来 不怎么会循序渐进 原谅俺 1 需求整理 如上
  • 什么是过拟合和欠拟合

    今天突然被以前同学人问到什么是机器学习中的 过拟合 过拟合就是训练的时候效果很好损失函数值可以降得很低 但是到测试数据集的时候表现就不那么好了 就是过分依赖于现有训练数据集的特征造成的 可以加大数据集来进行训练 比如在图像领域可以通过拉伸旋
  • Fabric核心模块

    Fabric由五个核心模块组成的程序组 1 peer 主节点模块 负责存储区块链数据 运行维护链码 2 orderer 交易打包 排序模块 3 cryptogen 组织和证书生成模块 4 configtxgen 区块和交易生成模块 生成创始
  • Linux Clock

    http www wowotech net pm subsystem clk overview html Linux common clock framework 1 概述 http www wowotech net pm subsyste
  • 500 Request processing failed; nested exception is java.lang.NullPointerException

    类型 异常报告 消息 Request processing failed nested exception is java lang NullPointerException 描述 服务器遇到一个意外的情况 阻止它完成请求 报错原因 这种报
  • TensorFlow2 Fashion-MNIST图像分类(二)

    1 图像分类数据的标准化 本篇内容是 TensorFlow2 Fashion MNIST图像分类 一 的第二部分 请先阅读第一部分中的内容 上一部分内容最后提到 模型训练结果出现不拟合现象 主要原因就在于特征数据没有进行标准化处理 因此本部
  • Unity3d Animation动画详解。。。

    动画脚本 Animation Scripting Unity s 动画系统允许你创建一个漂亮的动画蒙皮角色 动画系统支持动画融合 混合 添加动画 步调周期时间同步 动画层 控制动画回放的所有方面 时间 速度 混合权重 每个顶点有1 2 4个
  • 机器学习之下游任务

    下游任务是该领域称为那些利用预训练模型或组件的监督学习任务
  • EtherCat--主站开源的C语言库SOEM-环境搭建

    一 SOEM主站环境搭建 一 安装VS 二 下载SOEM 1 3 1 源代码 链接地址 http openethercatsociety github io 三 安装WinPcap 4 1 3 exe 链接地址 https www winp
  • 3DsMAX一渲染就卡解决方法

    目前比较常见的3DMAX卡死原因有三个 一是win10输入法不兼容 二是内存和CPU运行过载 三是场景模型问题 1 兼容问题 win10的微软拼音不兼容 常见但很难让人相信的问题 C4D渲染卡死也可以这样解决 解决方法 打开 windows
  • 二维坐标系的转换

    二维坐标系的变换分为旋转变换和平移变换 一 旋转变换 假设已知基坐标系XOY中的一点P x y 坐标原点为O 绕点O旋转 可以求得点P在新坐标系X OY 中坐标值 x y 如下图所示 求解x 和y 的关键是坚持用已知的边做斜边来求解 结合上
  • 华为OD机试 - 求字符串中所有整数的最小和(Java)

    题目描述 输入字符串s 输出s中包含所有整数的最小和 说明 字符串s 只包含 a z A Z 合法的整数包括 1 正整数 一个或者多个0 9组成 如 0 2 3 002 102 2 负整数 负号 开头 数字部分由一个或者多个0 9组成 如
  • power相关:(一)低功耗设计目的与功耗的类型

    一 低功耗设计的目的 1 便携性设备等需求 电子产品在我们生活中扮演了极其重要的作用 便携性的电子设备便是其中一种 便携性设备需要电池供电 需要消耗电池的能量 在同等电能提供下 低功耗设计的产品就能够工作更长的时间 时间的就是生命 因此低功
  • 【MySQL】使用Visio绘制数据库关系模型图

    使用Visio绘制数据库关系模型图 1 新建项目 文件 新建 软件和数据库 数据库模型图 点击后 出现如下界面 2 绘制 左侧 实体关系 中将 实体 形状拖放到绘制界面 如下图 3 编辑实体名称 如下图 4 编辑列 点击 列 如下图 完成实

随机推荐

  • 【因果推断与机器学习】Causal_inference: Chapter 4

    Chapter4 Estimation 一旦我们找到了识别因果量的策略 我们就需要选择如何使用统计方法估计这些因果量 我们使用受实际计算应用程序启发的示例来描述最常用的方法 首先 我们介绍因果估计的基础知识 如何从已确定的估计值到估计器 我
  • Oracle入门使用

    表空间的创建 普通用户授权 sys登录 grant create tablespace to 用户名 grant drop tablespace to 用户名 给user01 一个临时用户的角色 grant connect to user0
  • make -j时不知道cpu核数设置多大合适

    make j nproc nproc 就是你可以设置的当前机器的最大值了
  • Python使用Psycopg2访问PostgreSQL

    本文将关注 安装Psycopg2并使用其API访问PostgreSQL数据库 然后带您完成数据插入 数据检索 数据更新和数据删除 接下来 它将介绍事务管理 连接池和错误处理技术 以使用PostgreSQL开发健壮的python程序 使用pi
  • Vue性能优化

    Vue 项目性能优化 现在Vue3 0都快发布了为什么还要优化2 0的项目 因为市场上公司90 的项目全是Vue2 0的项目 迁移的话成本太高 所以只能进行性能的优化调整 废话就不多赘述了 直接开始吧 一 活用异步组件 Vue cli打包的
  • CXL 2.0 Device配置空间寄存器组成

    目录 1 配置空间 1 1 PCI Power Management Capability Structure 1 2 PCI Express Capability Structure 2 扩展配置空间 2 1 Virtual Channe
  • Java~实现简单的扑克牌,洗牌,发牌,玩捉鬼游戏(讲解详细,代码齐全)

    目录 一 扑克牌的定义 1 牌的定义 2 玩家的定义 二 扑克牌的操作 初始化 洗牌 发牌等 1 初始化 2 洗牌 3 发牌 三 初始化 洗牌 发牌代码 全 四 捉鬼游戏 首先一副扑克牌是54张 这里我们去掉大小王 也就是52张扑克牌 有
  • layui多文件上传_EasyUI的filebox组件实现多文件上传的步骤

    EasyUI的filebox组件实现多文件上传的步骤 新增回款记录详情里面有个添加回款记录文件的按钮 点击选择文件 文件既可以选择一个也可以选择多个 img 点击并拖拽以移动
  • 锁升级

    synchronized锁升级机制 普通对象对象头 Mark Word与锁息息相关 后面的Klass Word与锁无关 不讨论 Mark Word结构 public class Syn static int a 0 public stati
  • 服务器有时候显示美国,美国服务器不通的情况解决方法

    最近有朋友跟小编反馈美国服务器会出现不通的情况 这是什么原因呢 1 大网络环境的的原因 很多时候 我们是使用移动 电信和联通的线路 电信的是最常用的 突然网络不通的话 检查咨询下机房看是不是环境大网络出现什么异常 有的时候 美国得走的是国际
  • QT跨线程连接信号和槽

    对QT的信号和槽理解较为初级 今天通过多次尝试 终于学会了跨线程连接信号和槽 其基本方法如下 1 无参数传递时 1 定义 signal void signal1 2 连接部分写法 connect p class1 类1的指针 SIGNAL
  • WDK学习笔记_基于区块链溯源系统的后端接口开发

    文章目录 摘要 一 fabric go sdk各个封装函数的功能介绍 1 1 查询指定节点通道是否已经存在 函数 QuerySavedChannel 1 2 创建并加入通道 函数 CreateChannel 1 3 查询指定节点的指定链码是
  • Python类中的self到底是干啥的

    Python类中的self到底是干啥的 Python编写类的时候 每个函数参数第一个参数都是self 一开始我不管它到底是干嘛的 只知道必须要写上 后来对Python渐渐熟悉了一点 再回头看self的概念 似乎有点弄明白了 首先明确的是se
  • Vue语法(四)

    一 组件的自定义事件 1 一种组件间通信的方式 适用于 strong style color red 子组件 gt 父组件 strong 2 使用场景 A是父组件 B是子组件 B想给A传数据 那么就要在A中给B绑定自定义事件 span st
  • 2021-07-21训练日记upc联通数(思维)

    A 联通数 题目描述 数学高手小G最近发现了一种新型的数 他首先在草稿纸写下任意长度的数字串kkkkkkkkkkk 1 k 9 并在其中间添加加号 且相邻两个加号之间至少含有两个数字k 默认数字串第一个数字前与最后一个数字后也有两个加号 然
  • React+antdPro4+TS(typescript)相关(知识点)踩坑记录

    最近公司新开了项目 是后台管理系统 在技术选型上选择的时候 选择了react 然后整体的框架选择的是antd pro 4 这个脚手架搭建出来的单页面应用 要是想要仔细了解antd pro 可以看一下他的官方文档 antd pro 此次开发整
  • [c#][process]下发单个以及同时多条adb指令

    库 using System Diagnostics private Process cmd process 下发单条adb指令 代码示例 private string GetAdbCommandsArguments string cmds
  • fiddler设置只抓取某个网址的信息

    1 前提 在使用fiddler进行抓包过程中 如果想只抓取某一个地址下的请求 可以如下设置 2 设置 1 打开fiddler 选择过滤器 2 选择仅显示以下主机 3 点击 动作 选择 现在运行筛选器 4 打开百度 输入信息 进行搜索 查看抓
  • 字符分割算法研究

    字符分割 定义 以字符串的形式分割字符 达到将验证码分割的效果 从定位得到的车牌图像中分离出单个字符 包括汉字 字母和数字等 的图像 以便于字符分割 初步了解 行切分 字切分 参考范例 车牌识别 主要算法解析 改进的连通域分割法 版面分割是
  • 线程池基础知识

    1 为什么要使用线程池 线程池用什么用 创建线程和销毁线程的花销是比较大的 这些时间有可能比处理业务的时间还要长 这样频繁的创建线程和销毁线程 再加上业务工作线程 消耗系统资源的时间 可能导致系统资源不足 我们可以把创建和销毁的线程的过程去