当前位置:首页 > 科技  > 软件

Java为什么不建议使用Executors来创建线程池呢?

来源: 责编: 时间:2024-02-29 14:42:37 286观看
导读我们都知道在面试的过程中,关于线程池的问题,一直都是面试官比较注重的考点,现在也不会有面试官会选择去问创建线程都有哪些方式了,而更多的实惠关注到如何去使用线程池,今天了不起就来和大家说说线程池。Java创建线程池方

我们都知道在面试的过程中,关于线程池的问题,一直都是面试官比较注重的考点,现在也不会有面试官会选择去问创建线程都有哪些方式了,而更多的实惠关注到如何去使用线程池,今天了不起就来和大家说说线程池。rzQ28资讯网——每日最新资讯28at.com

Java创建线程池方式

在Java中,创建线程池主要使用java.util.concurrent包下的Executors类。这个类提供了几种静态工厂方法,用于创建和管理不同类型的线程池。以下是一些常见的创建线程池的方式:rzQ28资讯网——每日最新资讯28at.com

1.Fixed Thread Pool(固定线程池)

  • 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。
  • 创建方法:Executors.newFixedThreadPool(int nThreads)

2.Cached Thread Pool(缓存线程池)

  • 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
  • 创建方法:Executors.newCachedThreadPool()

3.Single Thread Executor(单线程执行器)

  • 创建一个使用单个工作线程的 Executor,以无界队列方式来运行该线程。(注意,如果单个线程始终因为等待新任务而处于非活动状态,则在现行线程终止之前,它可能无法终止。)但是,如果线程因为失败而终止,那么会有一个新的线程来替代它。单个线程的优势在于,你无需处理对线程生命周期的管理。
  • 创建方法:Executors.newSingleThreadExecutor()

4.Scheduled Thread Pool(计划线程池)

  • 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
  • 创建方法:Executors.newScheduledThreadPool(int corePoolSize)

5.自定义线程池

除了使用Executors类提供的静态工厂方法创建线程池外,还可以通过实例化ThreadPoolExecutor类来自定义线程池。这种方式提供了更多的灵活性,允许你设置线程池的核心参数,如核心线程数、最大线程数、线程存活时间、任务队列等。rzQ28资讯网——每日最新资讯28at.com

示例代码:rzQ28资讯网——每日最新资讯28at.com

import java.util.concurrent.*;    public class CustomThreadPool {      public static void main(String[] args) {          int corePoolSize = 5;          int maximumPoolSize = 10;          long keepAliveTime = 60L;          TimeUnit unit = TimeUnit.SECONDS;          BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();          ThreadFactory threadFactory = Executors.defaultThreadFactory();          RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();            ThreadPoolExecutor executor = new ThreadPoolExecutor(                  corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);            // 使用线程池执行任务...      }  }

非自定义线程池的缺点

我们先来看看 Executors 当中的几个方法,也就是上面了不起给大家写的除了自定义线程池的几个方法。rzQ28资讯网——每日最新资讯28at.com

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

在源码中有一个类,我们明显的看到了队列的身影,那就是 LinkedBlockingQueue。rzQ28资讯网——每日最新资讯28at.com

它实现了一个基于链接节点的可选容量的阻塞队列。此队列按 FIFO(先进先出)排序元素。队列的头部是在队列中存在时间最长的元素,队列的尾部是在队列中存在时间最短的元素。新元素总是插入到队列的尾部,而检索操作(如 take 和 poll)总是从队列的头部开始。rzQ28资讯网——每日最新资讯28at.com

LinkedBlockingQueue 是一个线程安全的队列,它内部使用了锁和条件变量来保证多线程环境下的正确性和一致性。因为它是阻塞队列,所以它可以用于生产者和消费者模型,在生产者线程和消费者线程之间传递数据。rzQ28资讯网——每日最新资讯28at.com

LinkedBlockingQueue 的主要特点就几个rzQ28资讯网——每日最新资讯28at.com

  • 容量可选
  • 阻塞操作
  • 非阻塞操作
  • 线程安全
  • 高效的并发性能

为什么说容量可选呢?因为我们如果单独使用这个LinkedBlockingQueue 那么你可以在创建 LinkedBlockingQueue 时指定一个容量,这将限制队列中可以存储的元素数量。如果未指定容量,则队列的容量将是 Integer.MAX_VALUE。当队列满时,任何尝试插入元素的线程都将被阻塞,直到队列中有空间可用。rzQ28资讯网——每日最新资讯28at.com

而阻塞操作则是他提供了阻塞的 put 和 take 方法。put 方法用于添加元素到队列中,如果队列已满,则调用线程将被阻塞直到队列有空闲空间。take 方法用于从队列中移除并返回头部元素,如果队列为空,则调用线程将被阻塞直到队列中有元素可用。rzQ28资讯网——每日最新资讯28at.com

public void put(E e) throws InterruptedException {        if (e == null) throw new NullPointerException();        // Note: convention in all put/take/etc is to preset local var        // holding count negative to indicate failure unless set.        int c = -1;        Node<E> node = new Node<E>(e);        final ReentrantLock putLock = this.putLock;        final AtomicInteger count = this.count;        ......        public E take() throws InterruptedException {        E x;        int c = -1;        final AtomicInteger count = this.count;        final ReentrantLock takeLock = this.takeLock;        takeLock.lockInterruptibly();        try {            while (count.get() == 0) {                notEmpty.await();            }            x = dequeue();            c = count.getAndDecrement();            if (c > 1).....

我们看一个使用LinkedBlockingQueue的示例:rzQ28资讯网——每日最新资讯28at.com

import java.util.concurrent.BlockingQueue;  import java.util.concurrent.LinkedBlockingQueue;    public class ProducerConsumerExample {      public static void main(String[] args) throws InterruptedException {          BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);            Thread producer = new Thread(() -> {              try {                  for (int i = 0; i < 10; i++) {                      System.out.println("Produced: " + i);                      queue.put(i);                      Thread.sleep(200); // 模拟生产耗时                  }              } catch (InterruptedException e) {                  Thread.currentThread().interrupt();              }          });            Thread consumer = new Thread(() -> {              try {                  while (true) {                      Integer item = queue.take();                      System.out.println("Consumed: " + item);                      Thread.sleep(500); // 模拟消费耗时                  }              } catch (InterruptedException e) {                  Thread.currentThread().interrupt();              }          });            producer.start();          consumer.start();            producer.join();          // 注意:这里的 consumer 线程是一个无限循环,所以它不会自然结束。          // 在实际应用中,你需要有一个明确的停止条件来结束消费者线程。      }  }

说到这里感觉说多了,我们回归正题,如果我们使用标准的 newCachedThreadPool 方法,如果线程数设置和任务数不能够配合起来,就比如说设置的线程数是一定的,这个时候,任务数量越多,就会慢慢的进入到队列LinkedBlockingQueue中,队列的话,任务越多,占用的内存越多,最终就非常容易耗尽内存,导致OOM。rzQ28资讯网——每日最新资讯28at.com

所以我们不推荐直接使用 Executors 来创建线程池,但是我们更推荐使用 ThreadpoolExecutor创建线程池。原因就是如下的几点:rzQ28资讯网——每日最新资讯28at.com

1.资源控制:ThreadPoolExecutor 允许你明确控制并发线程的最大数量,防止因为创建过多的线程而耗尽系统资源。通过合理地设置线程池的大小,可以平衡资源利用率和系统性能。rzQ28资讯网——每日最新资讯28at.com

2.线程复用:线程池中的线程可以被多个任务复用,这减少了在创建和销毁线程上花费的时间以及开销,提高了系统的响应速度。rzQ28资讯网——每日最新资讯28at.com

3.任务队列:ThreadPoolExecutor 内部维护了一个任务队列,当线程池中的线程都在工作时,新提交的任务会被放在队列中等待执行。这提供了一种缓冲机制,可以平滑处理突发的高并发任务。rzQ28资讯网——每日最新资讯28at.com

4.灵活性:ThreadPoolExecutor 提供了多种配置选项,如核心线程数、最大线程数、线程存活时间、任务队列类型等,这些选项可以根据具体的应用场景进行调整,以达到最佳的性能和资源利用率。rzQ28资讯网——每日最新资讯28at.com

5.异常处理:当线程池中的线程因为未捕获的异常而终止时,ThreadPoolExecutor 会创建一个新的线程来替代它,从而保持线程池的稳定性。此外,你也可以通过提供自定义的 ThreadFactory 来控制线程的创建过程,例如设置线程的名称、优先级、守护状态等。rzQ28资讯网——每日最新资讯28at.com

6.可扩展性:ThreadPoolExecutor 的设计是基于策略的,它使用了多个接口和抽象类来定义线程池的行为,这使得它很容易通过扩展或替换某些组件来适应不同的需求。rzQ28资讯网——每日最新资讯28at.com

7.与Java并发库集成:ThreadPoolExecutor 是 Java 并发库 java.util.concurrent 的一部分,这个库提供了丰富的并发工具和类,如锁、信号量、倒计时器、阻塞队列等,这些都可以与 ThreadPoolExecutor 无缝集成,简化多线程编程的复杂性。rzQ28资讯网——每日最新资讯28at.com

8.性能监控和调优:ThreadPoolExecutor 提供了一些有用的方法,如 getTaskCount()、getCompletedTaskCount()、getPoolSize() 等,这些方法可以帮助你监控线程池的运行状态,从而进行性能调优。rzQ28资讯网——每日最新资讯28at.com

所以你了解了么?rzQ28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-75332-0.htmlJava为什么不建议使用Executors来创建线程池呢?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 使用ConfuserEx代码混淆工具保护你的.NET应用程序

下一篇: 在Golang中简化日志记录:提升性能和调试效率

标签:
  • 热门焦点
  • 对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • 一加首款折叠屏!一加Open渲染图出炉:罕见单手可握小尺寸

    8月5日消息,此前就有爆料称,一加首款折叠屏手机将会在第三季度上市,如今随着时间临近,新机的各种消息也开始浮出水面。据悉,这款新机将会被命名为&ldquo;On
  • Rust中的高吞吐量流处理

    作者 | Noz编译 | 王瑞平本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库,还使用这些库实现了一个流处理程序
  • JavaScript学习 -AES加密算法

    引言在当今数字化时代,前端应用程序扮演着重要角色,用户的敏感数据经常在前端进行加密和解密操作。然而,这样的操作在网络传输和存储中可能会受到恶意攻击的威胁。为了确保数据
  • 这款新兴工具平台,让你的电脑效率翻倍

    随着信息技术的发展,我们获取信息的渠道越来越多,但是处理信息的效率却成为一个瓶颈。于是各种工具应运而生,都在争相解决我们的工作效率问题。今天我要给大家介绍一款效率
  • 破圈是B站头上的紧箍咒

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之每年的暑期档都少不了瞄准追剧女孩们的古偶剧集,2021年有优酷的《山河令》,2022年有爱奇艺的《苍兰诀》,今年却轮到小破站抓住了追
  • 造车两年股价跌六成,小米的估值逻辑变了吗?

    如果从小米官宣造车后的首个交易日起持有小米集团的股票,那么截至2023年上半年最后一个交易日,投资者将浮亏59.16%,同区间的恒生科技指数跌幅为52.78%
  • 超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 最薄的14英寸游戏笔记本电脑 Alienware X14已可以购买

    2022年1月份在国际消费电子展(CES2022)上首次亮相的Alienware新品——Alienware X14现在已经可以购买了,这款笔记本电脑被誉为世界上最薄的 14 英寸游戏笔
Top