精华内容
下载资源
问答
  • 由浅入深理解Java线程池及线程池的如何使用
    展开全文
  • 其中涉及到一些细节包括不同参数、不同队列、不同拒绝策略的选择、产生的影响和行为、为更好的使用线程池奠定知识基础,其中值得注意的部分我用粗体标识。Doug LeaExecutorService基于池化的线程来执...

    这篇文章结合Doug Lea大神在JDK1.5提供的JCU包,分别从线程池大小参数的设置、工作线程的创建、空闲线程的回收、阻塞队列的使用、任务拒绝策略、线程池Hook等方面来了解线程池的使用,其中涉及到一些细节包括不同参数、不同队列、不同拒绝策略的选择、产生的影响和行为、为更好的使用线程池奠定知识基础,其中值得注意的部分我用粗体标识。

    ccd7ce2d6403b22004a373d1e601d9f4.png

    Doug Lea

    ExecutorService基于池化的线程来执行用户提交的任务,通常可以简单的通过Executors提供的工厂方法来创建ThreadPoolExecutor实例。

    线程池解决的两个问题:1)线程池通过减少每次做任务的时候产生的性能消耗来优化执行大量的异步任务的时候的系统性能。2)线程池还提供了限制和管理批量任务被执行的时候消耗的资源、线程的方法。另外ThreadPoolExecutor还提供了简单的统计功能,比如当前有多少任务被执行完了。

    快速开始

    为了使得线程池适合大量不同的应用上下文环境,ThreadPoolExecutor提供了很多可以配置的参数和可被用来扩展的钩子。然而,用户还可以通过使用Executors提供的一些工厂方法来快速创建ThreadPoolExecutor实例。比如:

    1. 使用Executors#newCachedThreadPool可以快速创建一个拥有自动回收线程功能且没有限制的线程池。
    2. 使用Executors#newFixedThreadPool可以用来创建一个固定线程大小的线程池。
    3. 使用Executors#newSingleThreadExecutor可以用来创建一个单线程的执行器。

    如果上面的方法创建的实例不能满足我们的需求,我们可以自己通过参数来配置,实例化一个实例。

    关于线程数大小参数设置需要知道的

    ThreadPoolExecutor会根据corePoolSize和maximumPoolSize来动态调整线程池的大小:poolSize。

    当任务通过executor提交给线程池的时候,我们需要知道下面几个点:

    1. 如果这个时候当前池子中的工作线程数小于corePoolSize,则新创建一个新的工作线程来执行这个任务,不管工作线程集合中有没有线程是处于空闲状态。
    2. 如果池子中有比corePoolSize大的但是比maximumPoolSize小的工作线程,任务会首先被尝试着放入队列,这里有两种情况需要单独说一下:
    3. a、如果任务呗成功的放入队列,则看看是否需要开启新的线程来执行任务,只有当当前工作线程数为0的时候才会创建新的线程,因为之前的线程有可能因为都处于空闲状态或因为工作结束而被移除。
    4. b、如果放入队列失败,则才会去创建新的工作线程。
    5. 如果corePoolSize和maximumPoolSize相同,则线程池的大小是固定的。
    6. 通过将maximumPoolSize设置为无限大,我们可以得到一个无上限的线程池。
    7. 除了通过构造参数设置这几个线程池参数之外我们还可以在运行时设置。

    核心线程WarmUp

    默认情况下,核心工作线程值在初始的时候被创建,当新任务来到的时候被启动,但是我们可以通过重写prestartCoreThread或prestartCoreThreads方法来改变这种行为。通常场景我们可以在应用启动的时候来WarmUp核心线程,从而达到任务过来能够立马执行的结果,使得初始任务处理的时间得到一定优化。

    定制工作线程的创建

    新的线程是通过ThreadFactory来创建的,如果没有指定,默认的Executors#defaultThreadFactory将被使用,这个时候创建的线程将都属于同一个线程组,拥有同样的优先级和daemon状态。扩展配置ThreadFactory,我们可以配置线程的名字、线程组合daemon状态。如果调用ThreadFactory#createThread的时候失败,将返回null,executor将不会执行任何任务。

    空闲线程回收

    如果当前池子中的工作线程数大于corePoolSize,如果超过这个数字的线程处于空闲的时间大于keepAliveTime,则这些线程将会被终止,这是一种减少不必要资源消耗的策略。这个参数可以在运行时被改变,我们同样可以将这种策略应用给核心线程,我们可以通过调用allowCoreThreadTimeout来实现。

    选择合适的阻塞队列

    所有的阻塞队列都可以被用来存放任务,但是使用不同的队列针对corePoolSize会表现不同的行为:

    当池中工作线程数小于corePoolSize的时候,每次来任务的时候都会创建一个新的工作线程。

    当池中工作线程数大于等于corePoolSize的时候,每次任务来的时候都会首先尝试将线程放入队列,而不是直接去创建线程。

    如果放入队列失败,且当先池中线程数小于maximumPoolSize的时候,则会创建一个工作线程。

    下面主要是不同队列策略表现:

    直接递交:一种比较好的默认选择是使用SynchronousQueue,这种策略会将提交的任务直接传送给工作线程,而不持有。如果当前没有工作线程来处理,即任务放入队列失败,则根据线程池的实现,会引发新的工作线程创建,因此新提交的任务会被处理。这种策略在当提交的一批任务之间有依赖关系的时候避免了锁竞争消耗。值得一提的是,这种策略最好是配合unbounded线程数来使用,从而避免任务被拒绝。同时我们必须要考虑到一种场景,当任务到来的速度大于任务处理的速度,将会引起无限制的线程数不断的增加。

    无界队列:使用无界队列如LinkedBlockingQueue没有指定最大容量的时候,将会引起当核心线程都在忙的时候,新的任务被放在队列上,因此,永远不会有大于corePoolSize的线程被创建,因此maximumPoolSize参数将失效。这种策略比较适合所有的任务都不相互依赖,独立执行。举个例子,如网页服务器中,每个线程独立处理请求。但是当任务处理速度小于任务进入速度的时候会引起队列的无限膨胀。

    有界队列:有界队列如ArrayBlockingQueue帮助限制资源的消耗,但是不容易控制。队列长度和maximumPoolSize这两个值会相互影响,使用大的队列和小maximumPoolSize会减少CPU的使用、操作系统资源、上下文切换的消耗,但是会降低吞吐量,如果任务被频繁的阻塞如IO线程,系统其实可以调度更多的线程。使用小的队列通常需要大maximumPoolSize,从而使得CPU更忙一些,但是又会增加降低吞吐量的线程调度的消耗。总结一下是IO密集型可以考虑多些线程来平衡CPU的使用,CPU密集型可以考虑少些线程减少线程调度的消耗。

    选择适合的拒绝策略

    当新的任务到来的而线程池被关闭的时候,或线程数和队列已经达到上限的时候,我们需要去做一个决定,怎么拒绝这些任务。下面介绍一下常用的策略:

    ThreadPoolExecutor#AbortPolicy:这个策略直接抛出RejectedExecutionException异常。

    ThreadPoolExecutor#CallerRunsPolicy:这个策略将会使用Caller线程来执行这个任务,这是一种feedback策略,可以降低任务提交的速度。

    ThreadPoolExecutor#DiscardPolicy:这个策略将会直接丢弃任务。

    ThreadPoolExecutor#DiscardOldestPolicy:这个策略将会把任务队列头部的任务丢弃,然后重新尝试执行,如果还是失败则继续实施策略。

    除了上面的几种策略,我们也可以通过实现RejectedExecutionHandler来实现自己的策略。

    利用Hook嵌入你的行为

    ThreadPoolExecutor提供了protected类型可以被覆盖的钩子方法,允许用户在任务执行之前会执行之后做一些事情。我们可以通过它来实现比如初始化ThreadLocal、收集统计信息、如记录日志等操作。这类Hook如beforeExecute和afterExecute。另外还有一个Hook可以用来在任务被执行完的时候让用户插入逻辑,如rerminated。

    如果hook方法执行失败,则内部的工作线程的执行将会失败或被中断。

    可访问的队列

    getQueue方法可以用来访问queue队列以进行一些统计或者debug工作,我们不建议用作其他用途。同时remove方法和purge方法可以用来将任务从队列中移除。

    关闭线程池

    当线程池不在被引用并且工作线程数为0的时候,线程池将被终止。我们也可以调用shutdown来手动终止线程池。如果我们忘记调用shutdown,为了让线程资源被释放,我们还可以使用keepAliveTime和allowCoreThreadTimeOut来达到目的。

    写在最后

    JAVA本身提供的API已经可以让我们快速的进行基于线程池的多线程开发,但是我们必须要为我们写的代码负责,每一个参数的设置和策略的选择跟不同应用场景有绝对的关系。然而对于不同参数和不同策略的选择并不是一件容易的事情,我们必须要先回答一些基础问题:每创建一个线程,操作系统为我们做了哪些事情,这个线程的操作系统资源消耗主要在哪部分?假如我的应用场景是IO密集型的,那么我需要更多的线程还是更少的线程?假如我们的CPU操作和IO操作大概各占一半的话我们又需要如何选择?等等一些列问题。我认为、多线程开发是一件很容易的事情也是一件很不容易的事情。

    点击了解更多

    展开全文
  • JAVA线程池如何调优

    千次阅读 2020-08-16 10:28:12
    JAVA中,线程可以使用定制的代码来管理,应用也可以利用线程池。在使用线程池时,有一个因素非常关键:调节线程池的大小对获得最好的性能至关重要。线程池的性能会随线程池大小这一基本选择而有所不同,在某些条件...

    在JAVA中,线程可以使用定制的代码来管理,应用也可以利用线程池。在使用线程池时,有一个因素非常关键:调节线程池的大小对获得最好的性能至关重要。线程池的性能会随线程池大小这一基本选择而有所不同,在某些条件下,线程池过大对性能也有很多不利的影响。

    所有线程池的工作方式本质是一样的:有一个任务队列,一定数量的线程会从该任务队列获取任务然后执行。任务的结果可以发回客户端,或保存到数据库,或保存到某个内部数据结构中,等等。但是在执行完任务后,这个线程会返回任务队列,检索另一个任务并执行。

    线程池有最小线程数和最大线程数。池中会有最小数目的线程随时待命,等待任务指派给它们。因为创建线程的成本非常高昂,这样可以提高任务提交时的整体性能。线程池的最小线程数称作核心池大小,考虑ThreadPoolExecutor最简单的情况,如果有个任务要执行,而所有的并发线程都在忙于执行另一个任务,就会启动一个新线程,直到创建的线程达到最大线程数。

    一般我们会从以下几个方面对线程池进行设置:

    • 设置最大线程数

      对于给定硬件上的给定负载,最大线程数设置为多少最好呢?这个问题回答起来并不简单:它取决于负载特性以及底层硬件。特别是,最优线程数还与每个任务阻塞的频率有关。

      假设JVM有4个CPU可用,很明显最大线程数至少要设置为4。的确,除了处理这些任务,JVM还有些线程要做其他的事,但是它们几乎从来不会占用一个完整的CPU,至于这个数值是否要大于4,则需要进行大量充分的测试。

      有以下两点需要注意:

      一旦服务器成为瓶颈,向服务器增加负载时非常有害的;

      对于CPU密集型或IO密集型的机器增加线程数实际会降低整体的吞吐量;

    • 设置最小线程数

      一旦确定了线程池的最大线程数,就该确定所需的最小线程数了。大部分情况下,开发者会直截了当的将他们设置成同一个值。

      将最小线程数设置为其他某个值(比如1),出发点是为了防止系统创建太多线程,以节省系统资源。指定一个最小线程数的负面影响相当小。如果第一次就有很多任务要执行,会有负面影响:这是线程池需要创建一个新线程。创建线程对性能不利,这也是为什么起初需要线程池的原因。

      一般而言,对于线程数为最小值的线程池,一个新线程一旦创建出来,至少应该保留几分钟,以处理任何负载飙升。空闲时间应该以分钟计,而且至少在10分钟到30分钟之间,这样可以防止频繁创建线程。

    • 线程池任务大小

      等待线程池来执行的任务会被保存到某个队列或列表中;当池中有线程可以执行任务时,就从队列中拉出一个。这会导致不均衡:队列中任务的数量可能变得非常大。如果队列太大,其中的任务就必须等待很长时间,直到前面的任务执行完毕。

      对于任务队列,线程池通常会限制其大小。但是这个值应该如何调优,并没有一个通用的规则。若要确定哪个值能带来我们需要的性能,测量我们的真实应用是唯一的途径。不管是哪种情况,如果达到了队列限制,再添加任务就会失败。ThreadPoolExecutor有一个rejectedExecution方法,用于处理这种情况,默认会抛出RejectedExecutionExecption。应用服务器会向用户返回某个错误:或者是HTTP状态码500,或者是Web服务器捕获异常错误,并向用户给出合理的解释消息—其中后者是最理想的。

    • 设置ThreadPoolExecutor的大小

      线程池的一般行为是这样的:创建时准备最小数目的线程,如果来了一个任务,而此时所有的线程都在忙碌,则启动一个新线程(一直到达到最大线程数),任务就会立即执行。否则,任务被加入到等待队列,如果队列中已经无法加入新任务,则拒接之。

      根据所选任务队列的类型,ThreadPoolExecutor会决定何时会启动一个新线程。有以下三种可能:

      • SynchronousQueue

        如果ThreadPoolExecutor搭配的是SynchronousQueue,则线程池的行为和我们预期的一样,它会考虑线程数:如果所有的线程都在忙碌,而且池中的线程数尚未达到最大,则会为新任务启动一个新线程。然而这个队列没办法保存等待的任务:如果来了一个任务,创建的线程数已经达到最大值,而且所有的线程都在忙碌,则新的任务都会被拒绝,所以如果是管理少量的任务,这是个不错的选择,对于其他的情况就不适合了。

      • 无界队列

        如果ThreadPoolExecutor搭配的是无界队列,如LinkedBlockingQueue,则不会拒绝任何任务(因为队列大小没有限制)。这种情况下,ThreadPoolExecutor最多仅会按照最小线程数创建线程,也就是说最大线程池大小被忽略了。如果最大线程数和最小线程数相同,则这种选择和配置了固定线程数的传统线程池运行机制最为接近。

      • 有界队列

        搭配了有界队列,如ArrayBlockingQueue的ThreadPoolExecutor会采用一个非常负责的算法。比如假定线程池的最小线程数为4,最大为8所用的ArrayBlockingQueue最大为10。随着任务到达并被放到队列中,线程池中最多运行4个线程(即最小线程数)。即使队列完全填满,也就是说有10个处于等待状态的任务,ThreadPoolExecutor也只会利用4个线程。

        如果队列已满,而又有新任务进来,此时才会启动一个新线程,这里不会因为队列已满而拒接该任务,相反会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间。

        这个算法背后的理念是:该池大部分时间仅使用核心线程(4个),即使有适量的任务在队列中等待运行。这时线程池就可以用作节流阀。如果挤压的请求变得非常多,这时该池就会尝试运行更多的线程来清理;这时第二个节流阀—最大线程数就起作用了。

      对于上面提到的每一种选择,都能找到很多支持或反对的依据,但是在尝试获得最好的性能时,可以应用KISS原则"Keep it simple,stupid"。可以将最小线程数和最大线程数设置为相同,在保存任务方面,如果适合无界队列,则选择LinkedBlockingQueue;如果适合有界队列,则选择ArrayBlockingQueue。

    小结:

    • 有时对象池也是不错的选择,线程池就是情形之一:线程初始化的成本很高,线程池使得系统上的线程数容易控制。
    • 线程池必须需仔细调优,盲目的向池中添加新线程,在某些情况下对性能会有不利的影响。
    • 使用ThreadPoolExecutor时,选择更简单选项通常会带来最好的、最能预见的性能。

    如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!

    架构殿堂

    展开全文
  • 本文将介绍如何使用Java中的线程池执行任务。1 任务类型在使用线程池执行任务之前,我们弄清楚什么任务可以被线程池调用。按照任务是否有返回值可以将任务分为两种,分别是实现Runnable的任务类(无参数无返回值)和...

    在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率。线程池包含了一系列的线程,并且可以管理这些线程。例如:创建线程,销毁线程等。本文将介绍如何使用Java中的线程池执行任务。

    1 任务类型

    在使用线程池执行任务之前,我们弄清楚什么任务可以被线程池调用。按照任务是否有返回值可以将任务分为两种,分别是实现Runnable的任务类(无参数无返回值)和实现Callable接口的任务类(无参数有返回值)。在打代码时根据需求选择对应的任务类型。

    1.1 实现Runnable接口的类

    多线程任务类型,首先自然想到的就是实现 Runnable 接口的类,Runnable接口提供了一个抽象方法run,这个方法无参数,无返回值。例如:

    Runnable task = newRunnable() {

    @Overridepublic voidrun() {

    System.out.println("Execute task.");

    }

    };

    或者Java 8 及以上版本更简单的写法:

    Runnable task = ()->{

    System.out.println("Execute task.");

    };

    1.2 实现Callable接口的类

    于Runnable一样Callable也只有一个抽象方法,不过该抽象方法有返回值。在实现该接口的时候需要制定返回值的类型。例如:

    Callable callableTask = ()-> "finished";

    2 线程池类型

    java.util.concurrent.Executors 提供了一系列静态方法来创建各种线程池。下面例举出了主要的一些线程池及特性,其它未例举线程池的特性可由下面这些推导出来。

    2.1 线程数固定的线程池 Fixed Thread Pool

    顾名思义,这种类型线程池线程数量是固定的。如果线程数量设置为n,则任何时刻该线程池最多只有n个线程处于运行状态。当线程池中处于饱和运行状态时,再往线程池中提交的任务会被放到执行队列中。如果线程池处于不饱和状态,线程池也会一直存在,直到ExecuteService 的shutdown方法被调用,线程池才会被清除。

    //创建线程数量为5的线程池。

    ExecutorService executorService = Executors.newFixedThreadPool(5);

    2.2 可缓存的线程池 Cached Thread Pool

    这种类型的线程池初始大小为0个线程,随着往池里不断提交任务,如果线程池里面没有闲置线程(0个线程也表示没有闲置线程),则会创建新的线程,保证没有任务在等待;如果有闲置线程,则复用闲置状态线程执行任务。处于闲置状态的线程只会在线程池中缓存60秒,闲置时间达到60s的线程会被关闭并移出线程池。在处理大量短暂的(官方说法:short-lived)异步任务时可以显著得提供程序性能。

    //创建一个可缓存的线程池

    ExecutorService executorService = Executors.newCachedThreadPool();

    2.3 单线程池

    这或许不能叫线程池了,由于它里面的线程永远只有1个,而且自始至终都只有1个(为什么说这句话,因为要和 Executors.newFixedThreadPool(1) 区别开来),所以还是叫它“单线程池把”。你尽可以往单线程池中添加任务,但是每次只执行1个,且任务是按顺序执行的。如果前面的任务出现了异常,当前线程会被销毁,但1个新的线程会被创建用来执行后面的任务。以上这些和线程数只有1个的线程Fixed Thread Pool一样。两者唯一不同的是, Executors.newFixedThreadPool(1)可以在运行时修改它里面的线程数,而 Executors.newSingleThreadExecutor() 永远只能有1个线程。至于“为什么”,我准备专门再写一篇博客通过源代码来分析。

    //创建一个单线程池

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    2.4 工作窃取线程池

    扒开源码,会发现工作窃取线程池本质是 ForkJoinPool ,这类线程池充分利用CPU多核处理任务,适合处理消耗CPU资源多的任务。它的线程数不固定,维护的任务队列有多个,当一个任务队列完成时,相应的线程会从其它的任务队列中窃取任务执行,这也意味着任务的开始执行顺序并和提交顺序相同。如果有更高的需求,可以直接通过ForkJoinPool获取线程池。

    //创建一个工作窃取线程池,使用CPU核数等于机器的CPU核数

    ExecutorService executorService =Executors.newWorkStealingPool();//创建一个工作窃取线程池,使用CPU 3 个核进行计算,工作窃取线程池不能设置线程数

    ExecutorService executorService2 = Executors.newWorkStealingPool(3);

    2.5 计划任务线程池

    计划任务线程池可以按计划执行某些任务,例如:周期性的执行某项任务。

    //获取一个大小为2的计划任务线程池

    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);//添加一个打印当前线程信息计划任务,该任务在3秒后执行

    scheduledExecutorService.schedule(() -> { System.out.println(Thread.currentThread()); }, 3, TimeUnit.SECONDS);//添加一个打印当前线程信息计划任务,该任务在2秒后首次执行,之后每5秒执行一次。如果任务执行时间超过了5秒,则下一次将会在前一次执行完成之后立即执行

    scheduledExecutorService.scheduleAtFixedRate(() -> { System.out.println(Thread.currentThread()); }, 2, 5, TimeUnit.SECONDS);//添加一个打印当前线程信息计划任务,该任务在2秒后首次执行,之后每次在任务执行之后5秒执行下一次。

    scheduledExecutorService.scheduleWithFixedDelay(() -> { System.out.println(Thread.currentThread()); }, 2, 5, TimeUnit.SECONDS);//逐个清除 idle 状态的线程

    scheduledExecutorService.shutdown();//阻塞,在线程池被关调之前代码不再往下走

    scheduledExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

    3 使用线程池执行任务

    前面提到,任务类型分为有返回值和无返回值的类型,这里的调用也分为有返回值调用和无返回值的调用。

    3.1 无返回值任务的调用

    如果是无返回值任务的调用,可以用execute或者submit方法,这种情况下二者本质上一样。为了于有返回值任务调用保持统一,建议采用submit方法。

    //创建一个线程池

    ExecutorService executorService = Executors.newFixedThreadPool(3);//提交一个无返回值的任务(实现了Runnable接口)

    executorService.submit(()->System.out.println("Hello"));

    executorService.shutdown();

    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

    如果有一个任务集合,可以一个个提交。

    //创建一个线程池

    ExecutorService executorService = Executors.newFixedThreadPool(3);

    List tasks =Arrays.asList(

    ()->System.out.println("Hello"),

    ()->System.out.println("World"));//逐个提交任务

    tasks.forEach(executorService::submit);

    executorService.shutdown();

    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

    3.2 有返回值任务的调用

    有返回值的任务需要实现Callable接口,实现的时候在泛型位置指定返回值类型。在调用submit方法时会返回一个Future对象,通过Future的方法get()可以拿到返回值。这里需要注意的是,调用get()时代码会阻塞,直到任务完成,有返回值。

    ExecutorService executorService = Executors.newFixedThreadPool(2);

    Future future = executorService.submit(()->"Hello");

    System.out.println(future.isDone());//false

    String value =future.get();

    System.out.println(future.isDone());//true

    System.out.println(value);//Hello

    如果要提交一批任务,ExecutorService除了可以逐个提交之外,还可以调用invokeAll一次性提交,invokeAll的内部实现其实就是用一个循环逐个提交任务。invokeAll返回的值是一个Future List。

    ExecutorService executorService = Executors.newFixedThreadPool(2);

    List> tasks = Arrays.asList(()->"Hello", ()->"World");

    List> futures = executorService.invokeAll(tasks);

    invokeAny方法也很有用,线程池执行若干个实现了Callable的任务,然后返回最先执行结束的任务的值,其它未完成的任务将被正常取消掉不会有异常。如下代码不会输出“Hello”

    ExecutorService executorService = Executors.newFixedThreadPool(2);

    List> tasks =Arrays.asList(

    ()->{

    Thread.sleep(500L);

    System.out.println("Hello");return "Hello";

    }, ()->{

    System.out.println("World");return "World";

    });

    String s=executorService.invokeAny(tasks);

    System.out.println(s);//World

    输出:

    World

    World

    另外,在查看ExecutorService源码时发现它还提供了一个方法  Future submit(Runnable task, T result); ,可以通过这个方法提交一个实现了Runnable接口的任务,然后有返回值,而Runnable接口中的run方法时没有返回值的。那它的返回值是哪来的呢?其实问题在于该submit方法后面的一个参数,这个参数值就是返回的值。调用submit方法之后,有一通操作,然后直接把result参数返回了。

    ExecutorService executorService = Executors.newFixedThreadPool(1);

    Future future = executorService.submit(() -> System.out.println("Hello"), "World");

    System.out.println(future.get());//输出:World

    4 小结

    在利用多线程处理任务时,应该根据情况选择合适的任务类型和线程池类型。如果无返回值,可以采用实现Runnable或Callable接口的任务;如果有返回值,应该使用实现Callable接口的任务,返回值通过Future的get方法取到。选用线程池时,如果只用1个线程,用单线程池或者容量为1的固定容量线程池;处理大量short-live任务是,使用可缓存的线程池;若要有计划或者循环执行某些任务,可以采用计划任务线程池;如果任务需要消耗大量的CPU资源,应用工作窃取线程池。

    展开全文
  • 我是使用java线程池ThreadPoolExecutor,实现分批次去查询,查询到数据后,又分多个线程去做业务。 线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:ThreadPoolExecutor(int corePoolSize,...
  • 前言: 多线程的异步执行方式,虽然能够最大限度的发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担,线程本身...通俗点讲,当有工作任务过来时,就会想线程池拿一个线程,当工作完成后,并不是...
  • 前言掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会...github 地址github.com/whx123/Java…经典面试题面试问题1:Java线程池说一下,各个参数的作用,如何进行的?面试问题2:按线程池内部...
  • Java线程池使用方法

    千次阅读 2019-07-12 18:13:50
    由浅入深理解Java线程池及线程池的如何使用前言总体的架构研读ThreadPoolExecutor拒绝策略线程池的任务处理策略:线程池的关闭源码分析常见的四种线程池使用实例最后杂谈手动创建线程池有几个注意点 前言 多线程的...
  • 异步编程工具在Android开发中目前最被推荐的就是Kotlin协程,在引入Kotlin协程机制前,除了响应式扩展(RxJava)兼任异步编程工具外,Java API中线程与线程池就是最重要异步编程手段。而对于Android平台的Kotlin协程...
  • Java线程池使用

    2020-07-05 17:17:11
    《阿里巴巴Java开发手册》中强制线程不允许使用Executors显示创建线程,而是使用TreadPoolExecutor的方式,这种创建方式会明确线程池的运行规则,避免资源耗尽的风险。 TreadPoolExecutor 一共有四个构造方法,...
  • Java线程池使用总结

    2019-03-20 14:19:40
    Java线程池使用总结接口介绍功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一...
  • 之前写过一篇 Java 线程池使用介绍文章《线程池全面解析》,全面介绍了什么是线程池线程池核心类、线程池工作流程、线程池分类、拒绝策略、及如何提交与关闭线程池等。但在实际开发过程中,在线程池使用过程中...
  • java线程池使用

    2019-11-26 23:01:55
    为什么要用线程池? 我们知道java创建和...再者,系统的资源是有限的,线程又是稀缺资源,如果程序使用不当创建过多线程,有可能会拖垮整个系统,所以,线程池的另一个作用是管理线程,提高系统的稳定性。 如何创...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,986
精华内容 794
关键字:

java线程池如何使用

java 订阅