Java多线程线程池

概述

线程池就是定义管理几个有限的线程资源,进行循环利用。我们生活中有没有这种例子呢?当然是有的,比如我们银行窗口就是一个有限的资源,在我们处理完事情之后,将资源重新归还,然后重新分配下一个人员处理事情,这中模型就是线程池的模型。

使用线程池的好处

线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

几种常见的线程池

  • newFixedThreadPool(int nThreads)
    固定线程nThread大小的线程池,当线程超出最大时进入等待队列。

  • newCachedThreadPool()
    创建可变大小的线程池,根据实际需要调整线程池大小。

  • newScheduledThreadPool(int corePoolSize)
    创建corePoolSize个线程,可支持定时任务,周期性调度

  • newSingleThreadExecutor
    创建一个单一线程的线程池

线程池运行原理

首先我们先明确线程池由那几个部分组成

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}
  • corePoolSize,表示核心线程数,在线程池中最小活跃的线程数量。
  • maximumPoolSize,表示线程池中最大允许的线程数量。
  • keepAliveTime,计算多长时间没有新的任务提交线程池,将线程池中活跃线程数减少至最小线程。
  • unit,计算多长时间没有新任务提交线程池的时间单位
  • workQueue,线程池的等待队列,当线程池正在执行任务的线程达到核心线程数,等待队列没有满,则进入等待队列进行等待。
  • threadFactory ,创建线程工厂,这里我们通常用默认的就行了Executors.defaultThreadFactory()
  • RejectedExecutionHandler,拒绝策略,当线程池中已经到达最大线程数,并且等待队列也已经满了,则采取拒绝策略。
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

当一个线程任务提交过来是,将会进行如下判断

  1. 判断正在工作的线程是否小于corePoolSize,如果是则调用线程执行该任务。
  2. 如果大于corePoolSize,判断请求队列是否已满,没满则放入请求队列当中。
  3. 当请求队列满时,判断是否达到最大线程数maximumPoolSize,没到则创建线程从请求队列中执行任务。
  4. 当请求队列又满时,并且已经是最大线程数了,则进行拒绝策略。
    线程池执行过程

线程池中四种拒绝策略

线程池中所有的拒绝策略都是实现了RejectedExecutionHandler接口

策略名说明
AbortPolicy直接拒绝执行新的任务,并抛出异常RejectedExecutionException
DiscardPolicy不做任何处理,直接将任务丢失
DiscardOldestPolicy丢弃掉一个等待时间最长的任务,然后再尝试执行下一个任务,除非线程池已经停止
CallerRunsPolicy将任务转交给调用executor的线程,除非该线程已经被销毁,那么该任务也会被抛弃

阿里巴巴规约中注意

线程池中不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

使用Executors创建的线程池风险
  • FixedThreadPoolSingleThreadPool: 允许的请求队列是Integer.MAX_VALUE,可能会堆积大量的请求,从而导致oom
  • CachedThreadPoolScheduledThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致oom
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
Java多线程线程池是一种重用线程的机制,它可以管理和调度多个线程来执行任务。使用线程池可以提高程序的性能和效率,同时也可以避免线程的频繁创建和销毁。 在Java中,可以使用线程池类ExecutorService和Executors来创建和管理线程池。具体步骤如下: 1. 创建一个线程池对象:可以使用Executors提供的静态方法创建不同类型的线程池,比如newFixedThreadPool固定大小线程池、newCachedThreadPool缓存线程池等。 2. 向线程池提交任务:使用submit方法向线程池提交需要执行的任务,也可以使用execute方法提交任务。任务可以是实现了Runnable接口或Callable接口的类,也可以是Lambda表达式。 3. 线程池执行任务:线程池会根据线程池的规模和任务的数量来调度和执行任务,多个任务会并发执行。 4. 关闭线程池:当不再需要线程池时,可以调用线程池的shutdown方法来关闭线程池,确保所有的任务都被执行完毕。 使用线程池的好处有: 1. 提高性能:线程池可以重用线程,避免线程频繁创建和销毁的开销,提高程序的性能。 2. 提供线程管理和调度:线程池可以管理和调度线程,根据线程池的规模和任务的数量来调度和执行任务。 3. 控制资源的使用线程池可以限制并发线程的数量,避免过度占用系统资源。 在Java开发中,使用线程池是一种推荐的多线程编程方式,也是阿里巴巴在其《Java开发手册》中强制规定的做法。 Java线程创建是依赖于系统内核的,通过JVM调用系统库创建内核线程,内核线程Java Thread是1:1的映射关系。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Java多线程(六):线程池详解](https://blog.csdn.net/m0_59140023/article/details/124436385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [java多线程线程池](https://blog.csdn.net/qq_29996285/article/details/118955325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值