iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java多线程案例之线程池
  • 816
分享到

Java多线程案例之线程池

java线程池多线程 2023-09-04 17:09:51 816人浏览 安东尼
摘要

文章目录 一. 线程池概述1. 什么是线程池2. Java标准库提供的线程池 二. 线程池的简单实现 一. 线程池概述 1. 什么是线程池 线程池和和字符串常量池, 数据库连接池一样,

文章目录

一. 线程池概述

1. 什么是线程

线程池和和字符串常量池, 数据库连接池一样, 都是为了提高程序的运行效率, 减少开销; 随着并发程度的提高, 当我们去频繁的创建和销毁线程, 此时程序的开销还是挺大的, 为了进一步提高效率, 就引入了线程池, 程序中所创建的线程都会加载到一个 “池子” 中, 当程序需要使用线程的时候, 可以直接从池里面获取, 用完了就将线程还给池, 这样在多线程的环境中就不用去重复的创建和销毁线程, 从而使程序的运行效率提高, 线程池是管理线程的方式之一.

🎯那为什么从线程池中“拿”线程会比直接创建线程要更加高效呢?

这是因为创建线程和销毁线程, 是交由操作系统内核完成的, 而我们使用线程池调度线程是在用户态实现的(用户代码中就能实现的,不必交给内核操作);

如果将任务交给内核态, 就需要通过系统调用, 让内核来执行任务, 但此时你不清楚内核身上背负着多少任务(内核不是只给一个应用程序服务, 是要给所有的程序都提供服务), 当使用系统调用, 执行内核代码的时候, 无法确定内核都要做哪些工作, 整体过程"不可控"的;

相比于内核来说, 用户态, 程序执行的行为是可控的, 用户态只去完成你所指定的任务, 效率更高, 开销更小.

2. Java标准库提供的线程池

Java中提供了线程池相关的标准类ThreadPoolExecutor, 也被称作多线程执行器, 该类中的线程包括两类, 一类是核心线程, 另一类是非核心线程, 当核心线程都被占用还不能满足程序任务执行的需求时, 就会启用非核心线程, 直到任务量少了, 随之非核心线程也就会销毁.

jdk8中提供了4个构造方法, 这里主要介绍和理解参数最多的那一个构造方法, 其他构造方法只是基于这里的减少了参数而已.

public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler)

为了便于理解这里参数之间的关系, 我们使用生活中的例子来类比理解, 假设这里有一家公司:

  1. corePoolSize表示核心线程数, 公司的正式员工.
  • 🎯那核心线程数最合适值是多少呢? 假设CPU有N核心, 最适核心线程数是N? 是2N? 是1.5N? 只要你能够说出一个具体的数, 那就错了, 最适的核心线程数要视情况和业务场景而定, 没有一个绝对的标准的值.
  1. maximumPoolSize表示最大线程数,就是核心线程数与非核心线程数之和, 公司的正式员工和请来的零时工(非核心线程), 现有的工作正式工干不完时, 就会招来零时工帮忙干活.
  2. keepAliveTime非核心线程最长等待新任务的时间, 超过此时间, 该线程就会被销毁; 就是相当于零时工最长摸鱼时间, 公司里面是不养闲人的, 零时工长时间没有工作干就会被辞退了, 整体的策略, 正式员工保底, 临时工动态调节.
  3. unit上面参数的时间单位.
  4. workQueue线程池的任务队列(阻塞队列), 通过submit方法将任务注册到该队列中.
  5. threadFactory线程工厂, 线程创建的方案.
  6. handler拒绝策略, 描述了当线程池任务队列满了, 如果继续添加任务会以什么样的方式处理.

img

在Java标准库中提供了4个拒绝策略, 如下:

Modifier and TypeClass and Description
static classThreadPoolExecutor.AbortPolicy 如果任务太多, 队列满了, 直接抛出异常RejectedExecutionException .
static classThreadPoolExecutor.CallerRunsPolicy 如果任务太多, 队列满了, 多出来的任务, 谁加的, 谁负责执行.
static classThreadPoolExecutor.DiscardOldestPolicy 如果任务太多, 队列满了, 丢弃最旧的未处理的任务.
static classThreadPoolExecutor.DiscardPolicy 如果任务太多, 队列满了, 丢弃多出来的任务.

下面的是其他的几个构造方法:

public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize,  long keepAliveTime,  TimeUnit unit,  BlockingQueue<Runnable> workQueue)public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize,  long keepAliveTime,  TimeUnit unit,  BlockingQueue<Runnable> workQueue,  ThreadFactory threadFactory) public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize,  long keepAliveTime,  TimeUnit unit,  BlockingQueue<Runnable> workQueue,  RejectedExecutionHandler handler)

使用线程池时, 往往使用的是ExecutorServerce,ExecutorServerceThreadPoolExecutor所实现的一个接口, 其中最重要的一个方法是submit方法, 这个方法能够将任务交给线程池去执行.

img

下面列出一些常见的创建线程池的方法:

import java.util.concurrent.*;public class TestDemo {    public static void main(String[] args) {        //创建一个固定数量的线程池        // 1. 创建一个操作无界队列且固定大小线程池        ExecutorService pool1 = Executors.newFixedThreadPool(10);        //线程池中线程的数量是动态变化的        // 2. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存        ExecutorService pool2 = Executors.newCachedThreadPool();        //线程池中只有一个线程        // 3. 创建一个操作无界队列且只有一个工作线程的线程池        ExecutorService pool3 = Executors.newSingleThreadExecutor();        //线程池中只有一个线程+定时器功能        // 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。        ExecutorService pool4 = Executors.newSingleThreadScheduledExecutor(Executors.defaultThreadFactory());        //创建一个固定数量的线程池+定时器功能        // 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。        ExecutorService pool5 = Executors.newScheduledThreadPool(3, Executors.defaultThreadFactory());        // 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序        ExecutorService pool6 = Executors.newWorkStealingPool();        // 7. 自定义线程池        ExecutorService pool7 = new ThreadPoolExecutor(3,                10,                10000,                TimeUnit.MILLISECONDS,                new LinkedBlockingQueue<Runnable>());    }    }

观察上面代码中前6种创建方式, 都是使用Executors(线程池的工具类)调用一个方法返回一个对象来创建线程池对象, 与第7种直接new对象的方式不同, 通过前6种方式创建出来的线程池, 本质上也是通过包装ThreadPoolExecutor来实现出来的.

这种使用普通方法(一般是静态的)代替构造方法创建对象的思想就是 “工厂模式”, 我们称这样的方法为 “工厂方法”, 相当于是把new操作隐藏在了方法里面, 提供这个工厂方法的类, 称为 “工厂类”, “工厂模式” 也是 “设计模式” 的一种.

使用示例:

下面的代码中要注意lambda表达式变量捕获的问题.

import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestDemo22 {    public static void main(String[] args) {        ExecutorService pool = Executors.newFixedThreadPool(10);        for (int i = 0; i < 1000; i++) {            int n = i;//注意变量捕获            pool.submit(new Runnable() {                @Override                public void run() {                    System.out.println("hello" + n);                }            });            Thread.sleep(200);        }    }}

执行结果:

img

二. 线程池的简单实现

这里简单实现一个固定数量的线程池, 包含以下内容:

  • 任务, 可以直接使用Runnable实现.
  • 组织任务的数据结构, 使用阻塞队列BlockingQueue即可.
  • 若干个工作线程, 工作线程要通过一个循环不断的从阻塞队列中获取任务.
  • 注册任务的方法submit, 将任务添加到阻塞队列当中.

代码实现:

import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;class MyThreadPool {    //使用阻塞队列来保存任务    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();    //这里创建出若干个工作线程,n表示线程的数量    public MyThreadPool(int n) {        for (int i = 0; i < n; i++) {            Thread t = new Thread(() -> {                while (!Thread.interrupted()) {                    try {                        Runnable runnable = queue.take();                        runnable.run();                    } catch (InterruptedException e) {                        throw new RuntimeException(e);                    }                }            });            t.start();        }    }    //注册任务给线程池    public void submit(Runnable runnable) {        try {            queue.put(runnable);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }    }}

下面来测试一下这里实现的线程池:

public class TestDemo24 {    public static void main(String[] args) {        //创建10个线程        MyThreadPool pool = new MyThreadPool(10);        for (int i = 0; i < 20; i++) {            int n = i;//注意变量捕获的问题            pool.submit(new Runnable() {                @Override                public void run() {                    System.out.println("hello" + n);                }            });        }    }}

执行结果:

由于操作系统的随机调度, 这里的执行顺序是不固定的.

img

来源地址:https://blog.csdn.net/Trong_/article/details/128805874

--结束END--

本文标题: Java多线程案例之线程池

本文链接: https://www.lsjlt.com/news/394089.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java多线程案例之线程池
    文章目录 一. 线程池概述1. 什么是线程池2. Java标准库提供的线程池 二. 线程池的简单实现 一. 线程池概述 1. 什么是线程池 线程池和和字符串常量池, 数据库连接池一样,...
    99+
    2023-09-04
    java 线程池 多线程
  • 多线程案例(4)-线程池
    文章目录 多线程案例四四、线程池 大家好,我是晓星航。今天为大家带来的是 多线程案例-线程池 相关的讲解!😀 多线程案例四 四、线程池 线程池是什么 虽然创建线程 / 销毁线程 的开销 想象这么一个场景: ...
    99+
    2023-08-19
    java 开发语言 java-ee 多线程
  • Java多线程案例之定时器
    文章目录 一. 定时器概述1. 什么是定时器2. 标准库中的定时器 二. 定时器的简单实现 一. 定时器概述 1. 什么是定时器 定时器是一种实际开发中非常常用的组件, 类似于一个 “...
    99+
    2023-09-10
    java 定时器 Timer 多线程 线程安全
  • C#多线程之线程池(ThreadPool)
    一、简介 前面介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上来讲,这样做并不理想(因为每使用一个线程...
    99+
    2022-11-13
  • Java多线程之线程池七个参数的示例分析
    这篇文章主要介绍Java多线程之线程池七个参数的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交...
    99+
    2023-06-14
  • C#多线程系列之线程池
    目录线程池ThreadPool 常用属性和方法线程池说明和示例线程池线程数线程池线程数说明不支持的线程池异步委托任务取消功能计时器线程池 线程池全称为托管线程池,线程池受 .NET ...
    99+
    2022-11-13
  • Java多线程案例之阻塞队列
    文章目录 一. 认识阻塞队列1. 什么是阻塞队列2. 生产者消费者模型3. 标准库中阻塞队列类 二. 基于循环队列实现的简单阻塞队列1. 循环队列的简单实现2. 阻塞队列的简单实现 ...
    99+
    2023-09-01
    java 面试 阻塞队列 生产者消费者模型 多线程
  • 线程池之newFixedThreadPool定长线程池的实例
    newFixedThreadPool定长线程池的实例 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newFixedThr...
    99+
    2022-11-12
  • Java多线程之线程池七个参数详解
    目录corePoolSize:核心线程数maximumPoolSize:最大线程数keepAliveTime:空闲线程存活时间unit:时间单位workQueue:工作队列threa...
    99+
    2022-11-12
  • C#多线程之线程池ThreadPool详解
    一、ThreadPool概述 提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。 创建线程需要时间。如果有不同的小任务要完成,就可...
    99+
    2022-11-13
  • C#多线程之线程池ThreadPool用法
    目录一、ThreadPool1、QueueUserWorkItem()2、GetMaxThreads()3、GetMinThreads()4、SetMaxThreads()和SetM...
    99+
    2022-11-13
  • 线程池之newCachedThreadPool可缓存线程池的实例
    java线程池: Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,...
    99+
    2022-11-12
  • Java多线程案例之定时器详解
    目录一.什么是定时器二.标准库中的定时器(timer)2.1什么是定时器2.2定时器的使用三.实现定时器3.1什么是定时器3.2最终实现代码一.什么是定时器 定时器也是软件开发中的一...
    99+
    2022-11-13
    Java多线程 定时器 Java 定时器 Java 多线程
  • Python中线程池模块之多线程的示例分析
    这篇文章将为大家详细讲解有关Python中线程池模块之多线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、线程池模块引入from concurrent.futures i...
    99+
    2023-06-15
  • Java手写线程池之向JDK线程池进发
    目录前言JDK线程池一瞥自己动手实现线程池线程池参数介绍实现Runnable实现Callable拒绝策略的实现线程池关闭实现工作线程的工作实现线程池实现的BUG完整代码线程池测试总结...
    99+
    2022-11-13
    Java手写线程池 Java线程池
  • Java多线程常见案例分析线程池与单例模式及阻塞队列
    目录一、单例模式1、饿汉模式2、懒汉模式(单线程)3、懒汉模式(多线程)二、阻塞队列阻塞队列的实现生产者消费者模型三、线程池1、创建线程池的的方法(1)ThreadPoolExecu...
    99+
    2022-11-13
  • Java多线程案例之阻塞队列详解
    目录一.阻塞队列介绍1.1阻塞队列特性1.2阻塞队列的优点二.生产者消费者模型2.1阻塞队列对生产者的优化三.标准库中的阻塞队列3.1Java提供阻塞队列实现的标准类3.2Block...
    99+
    2022-11-13
    Java多线程阻塞队列 Java 阻塞队列 Java多线程
  • Java多线程之线程同步
    volatile 先看个例子 class Test { // 定义一个全局变量 private boolean isRun = true; // 从主线程调...
    99+
    2022-11-12
  • Java多线程与线程池技术分享
    目录一、序言1、普通执行2、线程池执行二、线程池基础1、核心参数2、参数与池的关系1、通用对比2、拓展对比3、无返回值任务4、有返回值任务三、Executors1、创建单一线程的线程...
    99+
    2022-11-13
  • Java多线程 自定义线程池详情
    主要介绍: 1.任务队列 2.拒绝策略(抛出异常、直接丢弃、阻塞、临时队列) 3.init( min ) 4.active 5.max ...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作