浅谈线程池

news/2024/11/15 2:03:24/

浅谈线程池

1、线程池

1.1、线程池介绍

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

1.2、线程池的优点

线程池有如下优势:

  • 避免频繁创建和销毁线程带来的资源浪费
  • 无需创建和销毁线程,提升响应速度
  • 提高线程的可管理性

1.3、创建线程池的方式

创建线程的方式有如下两种:

  • ThreadPoolExecutor
  • Executors

2、ThreadPoolExecutor

点进ThreadPoolExecutor 构造方法有四个,但是前面三个都是在最后一个上进行的包装处理,所以这里只针对最后一个构造方法进行说明。

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

先聊参数:

  • corePoolSize: 线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设为 true,如果线程池一直闲置并超过了 keepAliveTime 所指定的时间,核心线程就会被终止。
  • maximumPoolSize: 最大线程数,当线程不够时能够创建的最大线程数(包含核心线程数)

临时线程数 = 最大线程数 - 核心线程数

  • keepAliveTime: 线程池的闲置超时时间,默认情况下对非核心线程生效,如果闲置时间超过这个时间,非核心线程就会被回收。如果 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设为 true 的时候,核心线程如果超过闲置时长也会被回收。

  • unit: 配合 keepAliveTime 使用,用来标识 keepAliveTime 的时间单位,一般使用TimeUnit中的枚举常量,解释如下:

    TimeUnit.DAYS //天
    TimeUnit.HOURS //小时
    TimeUnit.MINUTES //分钟
    TimeUnit.SECONDS //秒
    TimeUnit.MILLISECONDS //毫秒
    TimeUnit.NANOSECONDS //毫微秒
    TimeUnit.MICROSECONDS //微秒

  • workQueue: 线程池中的任务队列,使用 execute() 或 submit() 方法提交的任务都会存储在此队列中。

    ThreadPoolExecutor线程池推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue和 ArrayBlockingQueue。

    1. SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。

    2. LinkedBlockingQueue:基于链表结构的无界阻塞队列,它可以指定容量也可以不指定容量(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)

    3. ArrayBlockingQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

  • threadFactory: 为线程池提供创建新线程的线程工厂。如果不指定线程工厂时,ThreadPoolExecutor 会使用ThreadPoolExecutor.defaultThreadFactory 创建线程。默认工厂创建的线程:同属于相同的线程组,具有同为 Thread.NORM_PRIORITY 的优先级,以及名为 “pool-XXX-thread-” 的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。

  • handler:表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略。(可以不指定)

    ThreadPoolExecutor 也提供了 4 种默认的拒绝策略:

    1. DiscardPolicy():丢弃掉该任务但是不抛出异常,不推荐这种(导致使用者没觉察情况发生)
    2. DiscardOldestPolicy():丢弃队列中等待最久的任务,然后把当前任务加入队列中。
    3. AbortPolicy():丢弃任务并抛出 RejectedExecutionException 异常(默认)。
    4. CallerRunsPolicy():由主线程负责调用任务的run()方法从而绕过线程池直接执行,既不抛弃 任务也不抛出异常(当最大线程数满了,任务队列中也满了,再来一个任务,由主线程执行)

示例:

// 新建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,5,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy()
);
// execute的使用
executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("hello, world!");}
});
// submit的使用
Future<String> submit = executor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("hello, world!");return "hello, world!";}
});
System.out.println(submit.get());

另外,ThreadPoolExecutor 还提供了许多的get()/set()方法

请添加图片描述
请添加图片描述

3、Executors

Executors 执行器创建线程池很多基本上都是在 ThreadPoolExecutor 构造方法上进行简单的封装,特殊场景根据需要自行创建。可以把Executors理解成一个工厂类。Executors可以创建6 种不同的线程池类型。

下面对这六个方法进行简要的说明:

  • newFixedThreadPool: 创建一个数量固定的线程池,超出的任务会在队列中等待空闲的线程,可用于控制程序的最大并发数。

    // 只需要传入一个参数,该参数将被用作核心线程数以及最大线程数,其他参数将采用默认选项
    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    
  • newCacheThreadPool: 短时间内处理大量工作的线程池,会根据任务数量产生对应的线程,并试图缓存线程以便重复使用,如果限制 60 秒没被使用,则会被移除缓存。如果现有线程没有可用的,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

    public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }
    
  • newScheduledThreadPool: 创建一个数量固定的线程池,支持执行定时性或周期性任务。

    // corePoolSize必传,threadFactory
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
    
  • newWorkStealingPool: Java 8 新增创建线程池的方法,创建时如果不设置任何参数,则以当前机器CPU 处理器数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。

    public static ExecutorService newWorkStealingPool() {return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);
    }
    
  • newSingleThreadExecutor: 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    // 可以指定线程工厂,也可以传入任何参数,将采用默认的线程工厂Executors.defaultThreadFactory()
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));
    }
    
  • newSingleThreadScheduledExecutor: 此线程池就是单线程的newScheduledThreadPool。

    // // 可以指定线程共从,也可以传入任何参数,将采用默认的线程工厂Executors.defaultThreadFactory()
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1, threadFactory));
    }
    

需要注意的是,在阿里巴巴Java开发手册中有这样的一条规定

请添加图片描述

也就是说,在实际上产中,应该尽量的避免用Excutors直接创建线程。


http://www.ppmy.cn/news/62907.html

相关文章

物联网|蓝牙4.0BLE协议栈简介|IAR集成开发环境简介|IAR各版本下载链接|物联网之蓝牙4.0 BLE基础-学习笔记(2)

文章目录 3、蓝牙4.0BLE协议栈简介Tips: BLE协议结构图介绍Tips IAR各版本下载链接 3、蓝牙4.0BLE协议栈简介 问题: 1、什么是LE协议栈?BLE协识栈与BLE协议的关系&#xff1f; 协议&#xff1a;机器之间的通讯标准&#xff0c;定义了一系列的通信标准&#xff0c;通信双方都按…

4. Mysql索引优化实战一

一条SQL在MySQL中是如何执行的 1. 示例表举一个大家不容易理解的综合例子 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 本节课内容&#xff1a; 索引下推优化详解Mysql优化器索引选择探究索引优化Order…

61.网页设计规则#7_元素之间的空白

为什么需要空白&#xff1f; 适度的空白使设计看起来整洁、现代和精致。空白传达不同片段信息之间的关系。空白意味着布局元素之间存在看不见的关系。 如何去使用空白&#xff1f; 各部分之间使用大量的空白&#xff1b; 元素组之间使用大量的空白。 在各个元素之间使用空白…

【Leetcode -405.数字转换为十六进制数 - 409.最长回文串】

Leetcode Leetcode -405.数字转换为十六进制数Leetcode - 409.最长回文串 Leetcode -405.数字转换为十六进制数 题目&#xff1a;给定一个整数&#xff0c;编写一个算法将这个数转换为十六进制数。对于负整数&#xff0c;我们通常使用 补码运算 方法。 注意 : 十六进制中所有…

cmake编译

cmake_minimum_required (VERSION 2.8)project (demo)##第一个是生成后的名称&#xff0c; 后面包含所有源文件 add_executable(main main.c testFunc.c)##cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中 aux_source_directory(. SRC_LIST) ##将当前位置的…

OpenHarmony送显流程分析

OpenHarmony送显流程分析 引言 本文档主要记录OpenHarmony在渲染完成之后如何进行合成和送显流程的。这个过程牵涉的代码很多&#xff0c;而且流程也是比较繁琐的。所以我一定要坚持下来。千万不能半途而废&#xff0c;也不要想着一口气吃出一个胖子&#xff0c;路漫漫其修远兮…

JAVA基础:Scanner类中next(), nextLine(), hasNext(), hasNextLine()

一、next() : 只读缓冲区中空格之前的数据,并且光标指向本行。二、nextLine() : 读取除回车以外的所有符号(整行内容)&#xff0c;光标定位在下一行三、hasNext() &#xff1a;检查下一个标记&#xff08;token&#xff09;&#xff0c;也就是以空格、制表符或换行符为分隔符的…

前端基于uniapp[uniPush]实现APP消息推送(安卓、IOS)

前提概述&#xff1a;此文章都是基于uniapp中uniPush2实现的在线、离线推送 app消息推送流程 登录开发者中心先填写好项目信息以及配置厂商在manifest.json文件中勾选推送模块在前端项目中创建云函数&#xff08;此云函数的作用是接受后台发送的消息模板&#xff0c;解析出来…