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

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

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

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

Java创建线程池方式

在Java中,创建线程池主要使用java.util.concurrent包下的Executors类。这个类提供了几种静态工厂方法,用于创建和管理不同类型的线程池。以下是一些常见的创建线程池的方式:vQp28资讯网——每日最新资讯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类来自定义线程池。这种方式提供了更多的灵活性,允许你设置线程池的核心参数,如核心线程数、最大线程数、线程存活时间、任务队列等。vQp28资讯网——每日最新资讯28at.com

示例代码:vQp28资讯网——每日最新资讯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 当中的几个方法,也就是上面了不起给大家写的除了自定义线程池的几个方法。vQp28资讯网——每日最新资讯28at.com

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

标签:
  • 热门焦点
  • 6月安卓手机性价比榜:Note 12 Turbo断层式碾压

    6月份有一个618,虽然这是京东周年庆的日子,但别的电商也都不约而同的跟进了,反正促销没坏处,厂商和用户都能满意。618期间一些产品也出现了历史低价,那么各个价位段的产品性价比
  • Automa-通过连接块来自动化你的浏览器

    1、前言通过浏览器插件可实现自动化脚本的录制与编写,具有代表性的工具就是:Selenium IDE、Katalon Recorder,对于简单的业务来说可快速实现自动化的上手工作。Selenium IDEKat
  • 三言两语说透设计模式的艺术-单例模式

    写在前面单例模式是一种常用的软件设计模式,它所创建的对象只有一个实例,且该实例易于被外界访问。单例对象由于只有一个实例,所以它可以方便地被系统中的其他对象共享,从而减少
  • 多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • JavaScript学习 -AES加密算法

    引言在当今数字化时代,前端应用程序扮演着重要角色,用户的敏感数据经常在前端进行加密和解密操作。然而,这样的操作在网络传输和存储中可能会受到恶意攻击的威胁。为了确保数据
  • 阿里大调整

    来源:产品刘有媒体报道称,近期淘宝天猫集团启动了近年来最大的人力制度改革,涉及员工绩效、层级体系等多个核心事项,目前已形成一个初步的&ldquo;征求意见版&rdquo;:1、取消P序列
  • 认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的&ldquo;网红&rdquo;。7月5日至9日,为期5天的东方甄选&ldquo;甘肃行&rdquo;首次在自有App内直播,
  • 与兆芯合作 联想推出全新旗舰版笔记本电脑开天N7系列

    联想与兆芯合作推出全新联想旗舰版笔记本电脑开天 N7系列。这个系列采用兆芯KX-6640MA处理器平台,KX-6640MA 处理器是采用了陆家嘴架构,16nm 工艺,4 核 4 线
  • 中关村论坛11月25日开幕,15位诺奖级大咖将发表演讲

    11月18日,记者从2022中关村论坛新闻发布会上获悉,中关村论坛将于11月25至30日在京举行。本届中关村论坛由科学技术部、国家发展改革委、工业和信息化部、国务
Top