android开发必知必会:java线程池(代码片段)

冬天的毛毛雨 冬天的毛毛雨     2023-01-13     690

关键词:

池化技术(Pool)

池化技术 (Pool) 是一种很常见的编程技巧,我们日常工作中常见的有数据库连接池、线程池、对象池等,它们的特点都是将“昂贵的”、“费时的”的资源维护在一个特定的“池子”中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。

线程池(Thread Pool)概念

顾名思义,线程池可以理解为一个装有线程的池子,这个池可以用来更好的统一管理线程,线程池技术可以帮我们管理线程,避免增加创建线程和销毁线程的资源损耗,我们可以通过线程池重复利用已有的线程,从而避免每次使用的时候都去创建。

线程池在 Java 中的抽象

线程池在Java中的抽象是 Executor 接口,但是真正的线程池实现其实是 ThreadPoolExecutor,该类提供了四个构造用于创建线程池,具体的方法会在下文中详细解释。除此之外,Java 还提供了几种常用的配置好的线程池,通过 Executors提供的工厂方法可以快速的创建线程池来使用,这些工厂方法其实也是直接或间接的使用 ThreadPoolExecutor 的构造方法来创建的线程池,具体的方法将在下文中详细解释。

ThreadPoolExecutor

ThreadPoolExecutor 是 Java 中对于线程池的具体实现,在 java.util.concurrent 包下。其提供了四个构造用于配置和创建线程池,这四个构造方法如下:

/**
 * @param corePoolSize    该线程池中核心线程数最大值
 * @param maximumPoolSize 该线程池中线程总数最大值
 * @param keepAliveTime   非核心线程闲置超时时长
 * @param unit            keepAliveTime 的单位
 * @param workQueue       线程池中的任务队列
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) 


/**
 * @param corePoolSize    该线程池中核心线程数最大值
 * @param maximumPoolSize 该线程池中线程总数最大值
 * @param keepAliveTime   非核心线程闲置超时时长
 * @param unit            keepAliveTime 的单位
 * @param workQueue       线程池中的任务队列
 * @param threadFactory   创建线程的工厂
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) 


/**
 * @param corePoolSize    该线程池中核心线程数最大值
 * @param maximumPoolSize 该线程池中线程总数最大值
 * @param keepAliveTime   非核心线程闲置超时时长
 * @param unit            keepAliveTime 的单位
 * @param workQueue       线程池中的任务队列
 * @param handler         饱和策略
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) 


/**
 * @param corePoolSize    该线程池中核心线程数最大值
 * @param maximumPoolSize 该线程池中线程总数最大值
 * @param keepAliveTime   非核心线程闲置超时时长
 * @param unit            keepAliveTime 的单位
 * @param workQueue       线程池中的任务队列
 * @param threadFactory   创建线程的工厂
 * @param handler         饱和策略
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

参数介绍

  • int corePoolSize
    线程池的核心线程数,默认情况下,核心线程会在线程池中一致存活,即使它们处于闲置状态。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由 keepAliveTime 所指定,当等待时间超过 keepAliveTime 所指定的时长后,核心线程就会被终止。

  • int maximumPoolSize
    线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

  • long keepAliveTime
    非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true 时,keepAliveTime 同样会作用于核心线程。

  • TimeUnit unit
    用于指定 keepAliveTime 参数的时间单位,这是一个枚举。

  • BlockingQueue workQueue
    线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个队列中。

  • ThreadFactory threadFactory
    线程工厂,为线程池提供创建新线程的功能。ThreadFactory 是一个接口,它只有一个方法:Thread newThread(Runnable r)。源码如下:

/**
 * An object that creates new threads on demand.  Using thread factories
 * removes hardwiring of calls to @link Thread#Thread(Runnable) new Thread,
 * enabling applications to use special thread subclasses, priorities, etc.
 *
 * <p>
 * The simplest implementation of this interface is just:
 *  <pre> @code
 * class SimpleThreadFactory implements ThreadFactory 
 *   public Thread newThread(Runnable r) 
 *     return new Thread(r);
 *   
 * </pre>
 *
 * The @link Executors#defaultThreadFactory method provides a more
 * useful simple implementation, that sets the created thread context
 * to known values before returning it.
 * @since 1.5
 * @author Doug Lea
 */
public interface ThreadFactory 

    /**
     * Constructs a new @code Thread.  Implementations may also initialize
     * priority, name, daemon status, @code ThreadGroup, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or @code null if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);

  • RejectedExecutionHandler handler
    饱和策略。当线程池无法执行新任务时,这可能是由于任务队列已满或者是无法成功执行任务,这个时候 ThreadPoolExecutor 会调用 handler 的 void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法来通知调用者,默认情况下 rejectedExecution() 方法会直接抛出一个 RejectedExecutionException 异常。RejectedExecutionHandler 源码如下:
/**
 * A handler for tasks that cannot be executed by a @link ThreadPoolExecutor.
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface RejectedExecutionHandler 

    /**
     * Method that may be invoked by a @link ThreadPoolExecutor when
     * @link ThreadPoolExecutor#execute execute cannot accept a
     * task.  This may occur when no more threads or queue slots are
     * available because their bounds would be exceeded, or upon
     * shutdown of the Executor.
     *
     * <p>In the absence of other alternatives, the method may throw
     * an unchecked @link RejectedExecutionException, which will be
     * propagated to the caller of @code execute.
     *
     * @param r the runnable task requested to be executed
     * @param executor the executor attempting to execute this task
     * @throws RejectedExecutionException if there is no remedy
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

ThreadPoolExecutor 为 RejectedExecutionHandler 提供了四个可选值:AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy,其中 AbortPolicy 是默认值,它会直接抛出一个 RejectedExecutionException 异常。详细解释:

  • AbortPolicy
    该策略是默认的拒绝策略,该策略将抛出未检查的 RejectedExecutionException,我们可以捕获这个异常,然后根据需求做相应的处理

  • CallerRunsPolicy
    直接在 execute 方法的调用线程中运行被拒绝的任务,除非执行程序已关闭,在这种情况下任务将被丢弃。

  • DiscardPolicy
    它默默地丢弃被拒绝的任务。

  • DiscardOldestPolicy
    丢弃最早的未处理请求,然后重试 execute。

执行任务的规则

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
  2. 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
  3. 如果在步骤2中无法将任务插入到任务队列中,这往往是由于队列任务已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
  4. 如果步骤3中的线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务, ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectedExecution() 方法来通知调用者。

线程池的分类

上文中提到通过 Executors提供的工厂方法可以快速的创建线程池来使用,那么 JDK 1.8 的Executors 总共可以归纳为提供了6种线程池的创建:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZjJjUzAE-1634115316459)(https://upload-images.jianshu.io/upload_images/27041669-b3b4224a2e45091d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

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


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

这是一种线程数固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于 FixedThreadPool 只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速的响应外界的请求。另外,任务队列也是没有大小限制的。

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


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

这是一种线程数量不定的线程池,该线程池只有非核心线程,并且最大线程数为 Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程都有超时机制,这个超时时间为60秒,超过该时间闲置线程就会被回收。和FixedThreadPool 不同的是,CachedThreadPool 的任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行,因为在这种场景下 SynchronousQueue 是无法插入任务的。SynchronousQueue 是一个非常特殊的队列,在很多情况下可以把它简单理解为一个无法存储元素的队列。该线程池比较适合执行大量的耗时较少的任务。当整个线程池都处于空闲状态时,线程池中的线程都会超时而被停止,这个时候 CachedThreadPool 之中实际是没有任务线程的,它几乎是不占用任何系统资源的。

  • ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 
    return new ScheduledThreadPoolExecutor(corePoolSize);


public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) 
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);

这是一个核心线程数量固定的,而非核心线程数无限制,并且当非核心线程闲置时会被立即回收的线程池。该线程池主要用于执行定时任务和具有固定周期的重复任务。

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


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

这是一个内部只有一个线程的线程池,它确保所有的任务都在同一个线程中按顺序执行。该线程池的意义在于统一所有外界任务到一个线程中,这使得这些任务之间不需要处理线程同步的问题。

  • SingleThreadScheduledExecutor
public static ScheduledExecutorService newSingleThreadScheduledExecutor() 
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));


public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1, threadFactory));

这是一个可以周期性执行任务的单线程线程池。

WorkStealingPool
public static ExecutorService newWorkStealingPool(int parallelism) 
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);


public static ExecutorService newWorkStealingPool() 
    return new ForkJoinPool
        (Runtime.getRuntime().availableProcessors(),
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);

这是JDK1.8引入的线程池,该线程池使用所有可用处理器作为目标并行度,创建一个窃取线程的池,拥有多个任务队列。其中一个任务可以产生其他较小的任务,这些任务被添加到并行处理线程的队列中,如果一个线程完成了工作并且无事可做,则可以从另一线程的队列中"窃取"工作。这个线程池不会保证任务的顺序执行,因为它的工作方式是抢占式的。该线程池主要使用了 ForkJoinPool来作为实现,基于 work-stealing 算法。

android单例模式必知必会(代码片段)

目录一、概念1.1单例类1.2优缺点1.2.1优点1.2.2缺点二、创建单例模式的方法2.1饿汉式2.2懒汉式2.2.1懒汉式(非线程安全)2.2.2懒汉式(线程安全)2.3双重检验锁2.4静态内部类2.5枚举小结三、扩展3.1防止反序列化3.2volatile关键字一、概念  ... 查看详情

泥瓦匠聊并发编程:线程与多线程必知必会(基础篇)

本文目录线程与多线程线程的运行与创建线程的状态1线程与多线程线程是什么?线程(Thread)是一个对象(Object)。用来干什么?Java线程(也称JVM线程)是Java进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thr... 查看详情

必知必会

...tSystem,关系数据库管理系统)应用软件之一。在Java企业级开发中非常常用,因为MySQL是开源免费的,并且方便扩展。  查看详情

并发编程:线程与多线程必知必会(代码片段)

1线程与多线程线程是什么?线程(Thread)是一个对象(Object)。用来干什么?Java线程(也称JVM线程)是Java进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。Java程序采用多线... 查看详情

大数据必知必会:hadoop单机环境安装(代码片段)

(大数据必知必会:Hadoop(1)单机环境安装)安装前准备操作系统准备本次安装采用的操作系统是Ubuntu20.04。更新一下软件包列表。sudoapt-getupdate安装Java8+使用命令安装Java8。sudoapt-getinstall-yopenjdk-8-jdk配置环境变量。vi~/.bashrcexportJAVA... 查看详情

android必知必会-app常用图标尺寸规范汇总(代码片段)

1.程序启动图标(iconlauncher)   放在mipmap-*dpi下,文件名为ic_launcher.pngL DPI(LowDensityScreen,120DPI),其图标大小为36x36pxM DPI(MediumDensityScreen,160DPI),其图标大小为48x48pxH DPI(HighDensityScr 查看详情

scala必知必会(代码片段)

...概述安装JavaVSScalaval和var基本数据类型lazy在Scala中的应用开发工具IDEAMaven函数方法定义默认参数命名参数可变参数条件语句循环语句面向对象概述类的定义和使用抽象类伴生类和伴生对象case和trait集合数组ListSetMapOptuon&Some&N... 查看详情

多线程必知必会的知识点

1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。2)... 查看详情

androidservice完全解析之必知必会(代码片段)

想必对于Android开发者来说,对Service一定不陌生了,作为大名鼎鼎的四大组件之一的service,在Android中有着不可替代的作用,它不像Activity那么光鲜亮丽,一般都是默默躲在后台执行着一些“见不得人的”任务&#... 查看详情

android必知必会-rgba转argb(代码片段)

...下发的颜色值字符串由于一开始依据iOS端的RGBA格式,Android端(Android使用ARGB方式)需要进行兼容,需要对此字符串转换。举例:RGBA#ABCDEF99=>ARGB#99ABCDEF方式①字符串截取和组合Stringargb 查看详情

android必知必会-recyclerview恢复上次滚动位置(代码片段)

如果移动端访问不佳,请访问–>Github版记录RecyclerView滚动位置并恢复是一个很常见的需求,通常需要精准恢复到上次的位置。预计会用到RecyclerView相关的三个知识点:监听RecyclerView滚动状态监听RecyclerView完成绘制... 查看详情

springmvc--必知必会(代码片段)

  SpringMVC基于模型--视图--控制器(Model-View-Controller,MVC)模式实现,属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无需实现任何接口。同时它还支持... 查看详情

大数据必知必会:hadoop伪分布式安装(代码片段)

(大数据必知必会:Hadoop(2)伪分布式安装)安装前准备操作系统准备本次安装采用的操作系统是Ubuntu20.04。更新一下软件包列表。sudoapt-getupdate安装Java8+使用命令安装Java8。sudoapt-getinstall-yopenjdk-8-jdk配置环境变量。vi~/.bashrcexportJAVA... 查看详情

mysql学习--mysql必知必会(代码片段)

?上图为数据库操作分类:??下面的操作參考(mysql必知必会)创建数据库运行脚本建表:mysql>createdatabasemytest;QueryOK,1rowaffected(0.07sec)mysql>showdatabases;+--------------------+|Database|+--------------------+|infor 查看详情

carson带你学java:关于string类的必知必会!(代码片段)

概述关于String类的必知必会主要包括:String的常用函数equals()与==的区别String、StringBuffer与StringBuilder的区别Switch能否用string做参数?1.String常用函数2.equals()与==的区别附:/***附1:Object的equalsÿ 查看详情

持续更新,建议收藏python必知必会的知识点,极大提升开发效率(代码片段)

问题本文主要介绍Python编程的一些必知必会的知识点,方便后续编程,提升效率。方法字符串转整数#字符串是普通整数a=int('1000')print(a)#1000#字符串是二进制b=int('1000',2)print(b)#8#整数转固定长度的二进制... 查看详情

jetty必知必会

导语如果是开发老鸟,请阅读快速入门,这已经足够。如果是新手,请阅读全文,这里有你想要的热部署,debug配置等内容,能够提高开发效率,避免浪费时间。简介jetty是一个应用服务器,类似于tomcat、resin,但是更轻量,尤其... 查看详情

android必知必会-app常用图标尺寸规范汇总(代码片段)

若移动端访问不佳,请使用–>Github版内容持续更新中,更新日期:2016-08-111.程序启动图标(iconlauncher)放在mipmap-*dpi下,文件名为ic_launcher.pngLDPI(LowDensityScreen,120DPI),其图标大小为36x36 查看详情