JAVA线程池

  • 1、什么是线程池

这里先简述一下,要执行一个线程(任务)时,不是new一个线程,而是从线程池获取一个存在的线程,执行完毕后,把该线程归还到线程池,等待下次执行任务。

  • 2、线程池的原理

线程池是一个对象池。对象池是一种常见的系统优化技术。其核心思想是,一个类被频繁使用时,没必要每次都初始化一个对象,可以将它的一些实例保存在一个容器中,使用时向容器请求,用完后归还容器,达到复用对象的目的,减少对象创建和销毁时的系统消耗。这里的容器就是对象池。线程池本质上是一个线程对象的对象池,由于对象的创建和销毁会有一定的耗时,使用线程池复用线程有利于提高系统性能。

对象池的使用场景:一些大型对象被频繁调用,大型对象指创建或销毁比较耗时的对象

常见的对象池有:线程池,数据库连接池。

  • 3、线程池的好处

1)降低资源消耗

从对象池的知识我们知道,线程池降低了线程创建和销毁的资源消耗,特别是一些耗时耗资源对象创建,比如数据库连接对象。

2)提高响应速度

线程池减少了创建和销毁线程池的时间,提高了响应速度

3)便于管理线程

线程池提供了关闭线程池、指定核心线程数等操作,方便了管理线程。

  • 4、默认实现

JAVA给我们定义好了几种常见的线程池:

1)生成固定线程数的线程池

     Executors.newFixedThreadPool(int nThreads)

2)生成一个可缓存的线程池

     Executors.newCachedThreadPool()

3)生成单线程的线程池

     Executors.newSingleThreadExecutor()

  • 5、线程池(Exector架构)的类图

核心类是ThreadPoolExecutor类,继承AbstractExecutorService抽象类(写了线程池的一些默认实现),AbstractExecutorService实现了ExecutorService接口(该接口定义了线程池的操作关闭、立即关闭、提交等操作),ExecutorService接口继承自Executor接口(定义execute方法)。

Executors类,在这里是工厂类。

 

 

  • 6、ThreadPoolExecutor类构造方法参数(引用自参考1
  • corePoolSize:线程池中的核心线程数;
  • maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程;
  • keepAliveTime:线程池中非核心线程闲置超时时长(准确来说应该是没有任务执行时的回收时间);

          一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉

          如果设置allowCoreThreadTimeOut(boolean value),则也会作用于核心线程

  • TimeUnit:时间单位。可选的单位有分钟(MINUTES),秒(SECONDS),毫秒(MILLISECONDS) 等;
  •  workQueue:任务的阻塞队列,缓存将要执行的Runnable任务,由各线程轮询该任务队列获取任务执行。可以选择以下几个阻塞队列。

       ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
       LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于              ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
         SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处 于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
        PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

  • ThreadFactory:线程创建的工厂。可以进行一些属性设置,比如线程名,优先级等等,有默认实现。
  • RejectedExecutionHandler:任务拒绝策略,当运行线程数已达到maximumPoolSize,队列也已经装满时会调用该参数拒绝任务,默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。

      AbortPolicy:直接抛出异常。
      CallerRunsPolicy:只用调用者所在线程来运行任务。
      DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
      DiscardPolicy:不处理,丢弃掉。
       当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

  • 7、ThreadPoolExecutor执行过程(参考文献2)

        corePoolSize->workQueue->maxPoolSize

       1) 当未超过核心线程数时,直接创建一个核心线程去执行任务

       2) 当超过核心线程数时,将任务加入workQueue中等待执行

       3) 当workQueue满时,在不超过maxPoolSize的情况下,启动线程去处理任务

       4、当线程数量超过maxPoolSize时,调用拒绝策略

      以下是流程图:

 

8、理解默认线程池实现

1) FixedThreadPool

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

FixedThreadPool,创建固定核心线程数的线程池。提交任务时,nThread<corePoolSize(corePoolSize=maxPoolSize),则创建核心线程,否则加入工作队列等待执行;当工作队列满时,则执行拒绝策略。

2)CachedThreadPool

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

CachedThreadPool,corePoolSize=0,maxPoolSize=MAX_Value。提交任务时,nThread永远>corePoolSize,同时SynchronousQueue是一个不存储元素的阻塞队列,所以CachedThreadPool提交任务时会尝试获取线程,若获取到则执行,若获取不到,则创建线程执行任务。任务执行完毕后,线程归还线程池,在无调用的情况下,存活时间是60s。

3)SingleThreadPool

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

SingleThreadPool,corePoolSize=1,maxPoolSize=1。该线程池只有一个核心线程,最多允许一个线程。若任务正在执行,新提交的任务将会进入阻塞队列等待执行。

 

  • 9、配置线程池大小

1)CUP密集型任务,就需要压榨CUP,N=Ncpu+1

2)IO密集型任务,参考值可为 N=2xNcpu

以上仅是参考值,最佳大小需要自己调试。

 

参考文献:

文献1:https://blog.csdn.net/u010983881/article/details/79322499

文献2:https://www.jianshu.com/p/e66e9924a953

文献3:https://www.cnblogs.com/dolphin0520/p/3932921.html

文献4:《JAVA程序性能优化》-葛一鸣

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池是一种用于管理和复用线程的机制,它可以提高多线程应用程序的性能和效率。线程池中的线程可以被重复使用,避免了频繁创建和销毁线程的销。 在Java中,线程池可以通过`ExecutorService`接口来创建和管理。线程池中的线程可以执行提交给它的任务,并且可以根据需要自动创建新的线程或销毁闲置的线程。 嵌套线程池是指在一个线程池中创建另一个线程池。这种情况通常发生在需要处理一些复杂的任务,其中每个任务本身也需要使用线程池来执行。 下面是一个示例代码,演示了如何在Java中嵌套使用线程池: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NestedThreadPoolExample { public static void main(String[] args) { // 创建外层线程池 ExecutorService outerThreadPool = Executors.newFixedThreadPool(5); // 提交任务给外层线程池 outerThreadPool.execute(() -> { // 创建内层线程池 ExecutorService innerThreadPool = Executors.newFixedThreadPool(3); // 提交任务给内层线程池 innerThreadPool.execute(() -> { // 内层线程池执行的任务 System.out.println("Inner thread pool task executed"); }); // 关闭内层线程池 innerThreadPool.shutdown(); }); // 关闭外层线程池 outerThreadPool.shutdown(); } } ``` 在上面的示例中,我们首先创建了一个外层线程池`outerThreadPool`,它使用`Executors.newFixedThreadPool()`方法创建了一个固定大小的线程池。然后,我们向外层线程池提交了一个任务,该任务在执行时创建了一个内层线程池`innerThreadPool`,并向内层线程池提交了一个任务。最后,我们分别关闭了内层线程池和外层线程池

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值