首页 > 其他分享 >线程池

线程池

时间:2024-09-19 16:04:21浏览次数:3  
标签:LinkedBlockingDeque 队列 任务 线程 new ThreadPoolExecutor


ThreadPoolExecutor构造方法

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

构造方法参数说明

  • corePoolSize
    核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
  • maximumPoolSize
    线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。
  • keepAliveTime
    非核心线程的闲置超时时间,超过这个时间就会被回收。
  • unit
    指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。
  • workQueue
    线程池中的任务队列.
    常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。
  • threadFactory
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("name-pool-%d").setDaemon(true).build();

可以按照此方法定制threadFactory

  • RejectedExecutionHandler

    当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。

new ThreadPoolExecutor.AbortPolicy()

线程池规则

线程池的线程执行规则跟任务队列有很大的关系。
当任务队列没有大小限制时:

  • 如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中。
  • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingDeque的时候,超过核心线程数量的任务会放在任务队列中排队。
  • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除。
  • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是LinkedBlockingDeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数。
  • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会执行线程池的拒绝策略。

当任务队列大小有限制时:

  • 当LinkedBlockingDeque塞满时,新增的任务会直接创建新线程来执行,当创建的线程数量超过最大线程数量时会执行线程池的拒绝策略。
  • SynchronousQueue没有数量限制。因为他根本不保持这些任务,而是直接交给线程池去执行。当任务数量超过最大线程数时会执行线程池的拒绝策略。

LinkedBlockingDeque:

ExecutorService executor=new ThreadPoolExecutor(8,16,0, TimeUnit.DAYS,new LinkedBlockingDeque<>(20),threadFactory,new ThreadPoolExecutor.AbortPolicy());

当线程数为8时,全部交由核心线程完成,当线程超过8时,放到队列里等待创建线程,当超过队列大小时,也就是有28个线程时,将会直接创建线程,当总线程数超过16时,也就是当等待队列和执行中线上加起来超过36(20+16)时,将会执行线程池的拒绝策略。

SynchronousQueue:

ExecutorService executor=new ThreadPoolExecutor(8,16,0, TimeUnit.DAYS,new SynchronousQueue<>(),threadFactory,new ThreadPoolExecutor.AbortPolicy());

当线程数为8时,全部交由核心线程完成,当线程超过8时,将会直接创建线程,当总线程数超过16时,将会执行线程池的拒绝策略。

个人推荐的线程池构造

一、用LinkedBlockingDeque,CallerRunsPolicy

  • 一般建议用CallerRunsPolicy,我们都不希望把任务丢失,但是如果实在是微不足道的任务,也可以抛出异常,但是要做好异常捕捉后的工作。除此以外,还有一个最佳实践是自己定义一个拒绝策略去实现RejectedExecutionHandler接口,实现rejectedExecution方法,完成重试等功能。
  • LinkedBlockingDeque的好处就是可以有一个队列可以缓冲,不至于全部以最大线程数去执行。
  • 下面这个线程池用于CPU密集型的,如果是IO密集型的话,可以把核心线程改成16,甚至更大(根据实际需要),最大线程数改成32,也不建议太大,那样会占用太多的线程数。
    private static final ExecutorService pool = new ThreadPoolExecutor(8, 16, 0, TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(1024),
            new ThreadFactoryBuilder().setNameFormat("message-pool-%s").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

二、用SynchronousQueue,CallerRunsPolicy
SynchronousQueue的好处在于不占用队列空间,直接由线程池运行,缺点在于少了缓冲的空间。下面这个还是CPU密集型的,如果是IO密集型参考上面。

    private static final ExecutorService pool = new ThreadPoolExecutor(8, 16, 0, TimeUnit.SECONDS,
            new SynchronousQueue<>(),
            new ThreadFactoryBuilder().setNameFormat("message-pool-%s").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

线程池的实现原理

ThreadPoolExecutor类里有一个线程集合HashSet workers和一个阻塞队列BlockingQueue workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workers中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行。这样也可以解释了为什么核心线程是永远不会死亡的,因为它会不断地去获取任务。

那么非核心线程是如何做到超时退出的呢?
其实我们可以注意下,任务是存在队列里的,队列的poll方法可以设置超时时间,这样我们去队列里获取任务的时候如果超时了就会返回一个失败的状态,这样如果在设置的超时时间范围内非核心线程获取不到任务就会自动退出并销毁。
具体是在源码的getTask()方法里:

try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }

标签:LinkedBlockingDeque,队列,任务,线程,new,ThreadPoolExecutor
From: https://www.cnblogs.com/leecoder5/p/18420780

相关文章

  • 【Java-线程池】
    目录什么是线程池:线程池七大参数:参数详细介绍:1.核心线程数:2.最大线程数:3.线程存活时间:4.时间单位:5. 阻塞工作队列:6.线程工厂:7.拒绝策略:线程池的执行流程:线程池的常用方法:线程池分类:线程池的状态:​编辑什么是线程池:    简单来说,线程池就是帮助我们......
  • 进程和线程基础内容
    什么是进程和线程?进程是指运行中的程序,在windows里可以在任务管理器中查看;线程是CPU调度的基本单位,进程可以看成程序执行的一个实例。一个进程可以拥有多个线程,每个线程使用其所属进程的栈空间。进程就是线程的容器,需要线程利用进程中的一些资源,处理一个代码、指令。最终实现进......
  • 操作系统:线程间通信方式(上):锁机制详解
    操作系统:线程间通信方式(上):锁机制详解在多线程编程中,多个线程共享资源是常见的需求,但资源竞争会导致数据不一致和冲突问题。锁机制是一种用于控制线程对共享资源访问的同步方式,确保同一时刻只有一个线程能够访问临界区(CriticalSection)。本文将详细介绍线程间通信中的锁机......
  • 优化下载性能:使用Python多线程与异步并发提升下载效率
    文章目录......
  • 线程池的执行流程、状态及类型
    线程池的理解        线程池是用于管理和复用线程的一种技术,可以优化线程的执行效率。因为,频繁创建和销毁线程,会带来额外的系统开销,而线程池可以通过创建并重用一组线程来执行任务,可以显著减少这种开销,提高程序的性能和响应能力。         线程池内部维护......
  • 线程池的执行流程是什么?(核心参数、执行流程、拒绝策略)
    一、线程池的执行流程1.首先,向线程池提交一个线程任务,线程池会分配空闲线程去处理该线程任务。2.如果没有空闲线程就判断当前存活线程数是否超过核心线程数:    (1)没有超过就创建一个核心线程,处理线程任务。        (2)超过核心线程数就将存活线程存放在工......
  • springboot中如何使用线程池
    springboot中如何使用线程池在SpringBoot中使用线程池,你可以定义一个ThreadPoolTaskExecutor的Bean,然后在需要的地方使用@Autowired注入这个Bean。以下是一个配置线程池的例子:importorg.springframework.context.annotation.Bean;importorg.springframew......
  • 《深入理解 Java 线程池:高效管理线程的利器》
    线程池1.什么是线程池?​线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。2.线程池常用类和接口​在Java标......
  • Java多线程-(线程的创建,线程安全,线程状态)
    第一章.创建线程的方式1.第一种方式_extendsThread1.定义一个自定义线程类继承Thread2.重写run方法(run方法是用于设置线程任务的)3.创建自定义线程类对象4.调用Thread类中的start方法(start方法:开启线程,jvm自动执行run方法)publicclassMyThreadextendsThread{......
  • 多线程下载nginx站点目录下文件
    代码如下importosimporttimeimportrequestsfrombs4importBeautifulSoupfromurllib.parseimporturljoinfromconcurrent.futuresimportThreadPoolExecutor,as_completedfromtqdmimporttqdmbase_url=""#要下载文件的基础URLdownload_dir="&......