JavaEE——介绍并简单使用线程池

news2024/11/16 13:28:40

文章目录

  • 一、 什么是线程池
  • 二、Java中线程池的运用
      • 1. 创建线程池中的问题
      • 2. 标准库中线程池的使用
  • 三、自主实现一个简单的线程池

一、 什么是线程池

所谓线程池,其实和字符串常量池,数据库连接池十分相似,就是设定一块区域,提前放好一些已经成型的线程,当需要使用的时候,直接进行调用不需要经历复杂的创建过程销毁线程时也不需要经历复杂过程,直接将线程返还给线程池即可线程池最大的优势就是减少了每次启动、销毁线程的损耗!

为什么引入线程池后创建/销毁会更加高效?
其实,创建/销毁线程这个操作时由操作系统内核完成的。而从线程池获取/放回用户代码即可实现,无需交给操作系统内核。

解释操作系统内核:
对于操作系统内核,下面我通过一个场景进行简单的介绍。

在这里插入图片描述
如上图所示,假设一个银行,有很多人要办业务,这些人大致分为两类,同样也对应两种不同的需求:

  • 第一类,这些人不是要在柜台办理什么业务,而是取钱(取款机),看银行项目等其他事务的,是自由的,类似于程序中的 用户态 用户态执行的是程序员自己实现的代码,在这里代码想干什么,怎么干都是程序员自主决定的。
  • 第二类,就是要办理业务的人,像是办理银行卡,更改用户名等。此时这些操作是需要在银行柜台后进行,你不能进入其中,需要通过工作人员间接执行。这就像是程序中的 内核态 内核态中的操作都是在操作系统内核中完成的,内核会为程序员提供一些 api,即,系统调用。 程序员通过这些系统调用来驱使内核完成工作,但是,这里面的工作是不受程序员控制,是内核自行完成的。

总的来讲,用户态中程序的行为是可控的,比较快速整洁。但是,内核态就需要过另外一个人 (操作系统内核) 的手续,就不可控了。

二、Java中线程池的运用

1. 创建线程池中的问题

创建一个线程池

在这里插入图片描述
如上图所示,这样就创建了一个数目固定为10的一个线程池。需要注意的是,这里划红线的地方,此处的 new 是方法名字的一部分,不是 new 关键字。

这个操作,就是使用某个静态的方法,直接构造一个对象出来(相当于将 new 方法隐藏在这个方法里)

这种方法称之为“工厂方法”,即利用“工厂模式”设计出的方法。

解释工厂模式:

所谓 工厂模式 简单来讲就是,使用普通的方法来代替构造方法创建对象。会出现这种模式的主要原因就是构造方法中有很多不便之处。
例如,现在有个类要表示平面上的一点,如图所示:
在这里插入图片描述
此时很明显就出了问题,正常来讲,多个构造方法是通过 “重载” 的形式提供的。但是,我们要明确的是,重载的要求是,方法名相同,参数的 个数/类型 不相同。 在这里很明显都不满足。
因此,这里才需要引入工厂模式,如图:
在这里插入图片描述
这样问题就得以解决了。

2. 标准库中线程池的使用

代码展示:

    public static void main(String[] args) {
        ExecutorService poll = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            poll.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello"+n);
                }
            });
        }
    }

这里的 submit 方法可以给线程池提供若干个任务。
运行之后不难发现,main 线程结束但是整个进程没有结束,在这里,线程池中的线程都是前台线程,因此会阻止进程结束。
这里要注意的是,当前线程池中存放了 1000 个任务。这 10 个线程分别分配执行,执行结束后就到线程池中获取,但是,不一定是平均分配,可能有的多有的少。

思考:

在这里插入图片描述
如上图所示,我们知道,这里的 n 就是起到一个中介的作用,但是为什么直接使用 i 却不行?

其实不难理解,这里主线程 for 中的 i 是一个局部变量,主线程执行的速度很快,这里的 for 执行完了,但是当前 run 的任务在线程池中还没有排到,此时 i 已经销毁了

解决这个问题的思路其实也不难理解,这里其实就是一个变量捕获。我们知道,这里的 run 方法属于 runnable。这个方法的执行不是立刻马上,而是在未来的某个节点才去执行。
所以,为了避免作用域的差异,导致后续执行时 i 已经被销毁,于是也就有了变量捕获,即就是在定义 run 方法时,将 i 的值记录下来。后续执行的时候将其复制过去即可。

Executors创建线程的几种方式:

对于创建线程池,大致有下面几种类型:

  • newCachedThreadPool() : 线程数量是动态变化的,根据任务多少的情况来创建线程。
  • newFixedThreadPool() : 设定一个恒定大小数量的线程池。
  • newSingleThreadExecutor() : 线程池中只有一个线程。
  • newScheduleThreadPool() : 类似于定时器,让任务延时执行,只是在执行的时候不使用扫描线程,而是使用单独的线程池来执行。

创建形式示例:

ExecutorService poll = Executors.newFixedThreadPool(10);

理解 ThreadPoolExecutor 构造方法参数

在这里插入图片描述

如上图所示,我们可以看到该构造方法中有很多参数,下面我来进行简单的解释。

首先,将创建一个线程池设想成开公司。每一个员工相当于一个线程。

  • corePoolSize: 正式员工数量(一旦录用,永不辞退)—— 核心线程数
  • maximumPoolSize: 正式员工 + 临时工数目。(临时工:一段时间不干活,就被辞退)
  • keepAliveTime: 临时工允许空闲的时间。
  • unit: keepAliveTime 的时间单位是秒,分钟,其他值…
  • workQueue: 传递任务的阻塞队列
  • threadFactory: 创建线程的工厂,参与具体创建线程的工作。
  • RejectedExecutionHandler: 拒绝策略, 如果任务量超出公司的负荷了接下来怎么处理.
    AbortPolicy(): 超过负荷, 直接抛出异常.
    CallerRunsPolicy(): 调用者负责处理
    DiscardOldestPolicy(): 丢弃队列中最老的任务.
    DiscardPolicy(): 丢弃新来的任务.

创建形式示例:

ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, 
                                              new SynchronousQueue<Runnable>(),
                                              Executors.defaultThreadFactory(),
                                              new ThreadPoolExecutor.AbortPolicy());

三、自主实现一个简单的线程池

根据前面的示例我们可以知道,实现一个线程池内部至少有两大部分

  1. 阻塞队列,用于保存任务。
  2. 实现若干个工作线程

代码展示

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

class MyThreadPool{
    //创建一个阻塞队列
    private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();

    // n 表示线程的数量
    public void MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()->{
                //不断尝试获取队列中的元素
                while(true){
                    try {
                        Runnable runnable = queue.take();
                        //调用 runnable 方法中的 run
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }

    //添加元素到线程池(阻塞队列)
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool = new MyThreadPool();

        //设定线程池的数量
        myThreadPool.MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            //创建任务放到线程池
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello: "+n);
                }
            });
        }
    }
}

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/734835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

打印机常见故障解决参考方法

1、首先检查打印机电源线连接是否可靠或电源指示灯是否点亮&#xff0c;然后再次打印文件&#xff0c;仍不能打印&#xff0c;请看下一步。 2、检查打印机与计算机之间的信号传输线是否可靠连接&#xff0c;检查并重新连接&#xff0c;如果打印机仍不能打印&#xff0c;请看下一…

Java线程Thread类常用方法

文章目录 1. start()&#xff1a;启动线程&#xff0c;使其执行run()方法中的代码。2. run()&#xff1a;线程的执行逻辑&#xff0c;需要在该方法中定义线程要执行的代码。3. sleep(long millis)&#xff1a;使当前线程暂停指定的毫秒数&#xff0c;进入阻塞状态。4. join()&a…

【C++】红黑树封装map和set

文章目录 一、map和set源码剖析二、红黑树的迭代器1.begin()与end()2.operator()与operator--() 三、set的模拟实现四、map的模拟实现五、完整代码实现1.RBTree.h2.set.h3.map.h5.Test.cpp 一、map和set源码剖析 我们知道&#xff0c;map和set的底层是红黑树&#xff0c;但是我…

如何用Python快速搭建一个文件传输服务

当我的朋友需要把他电脑上面的文件从他的电脑传递到我电脑上的时候&#xff0c;我只需要启动服务 启动服务&#xff01; 他打开web界面 就能把文件传递到我电脑上&#xff08;还能够实时显示进度&#xff09; 文件就已经在我电脑上的uploads文件夹里面了 项目结构如下 templat…

python浮点运算不准确

1.问题 1.12.2的最后结果并不等于3.3 2. 解决方法 错误示范 引入了Decimal计算&#xff0c;但是计算类型是float型&#xff0c;依然计算不准确 正确解决方法 把类型转化为字符串引入计算

动手实战 | 使用 Transformers 包进行概率时间序列预测

最近使用深度学习进行时间序列预测而不是经典方法涌现出诸多创新。本文将为大家演示一个基于 HuggingFace Transformers 包构建的概率时间序列预测的案例。 概率预测 通常&#xff0c;经典方法针对数据集中的每个时间序列单独拟合。然而&#xff0c;当处理大量时间序列时&…

spring中的扩展点解析以及实践使用

文章目录 1、ApplicationContextInitializer2、BeanDefinitionRegistryPostProcessor3、BeanFactoryPostProcessor4、InstantiationAwareBeanPostProcessor5、SmartInstantiationAwareBeanPostProcessor6、BeanFactoryAware7、ApplicationContextAwareProcessor8、BeanNameAwar…

查找文件所在的具体位置

Linux Command 命令: find – walk a file hierarchy (遍历文件层次结构) ;DESCRIPTION 描述: 在指定目录下查找文件和目录, 可以使用不同的选项来过滤和限制查找的结果 ; Grammar Format $ find <在哪个路径下查找> <可选参数…> 常用选项 -name <pattern>…

【javaEE面试题(四)线程不安全的原因】【1. 修改共享数据 2. 操作不是原子性 3. 内存可见性 4. 代码顺序性】

4. 多线程带来的的风险-线程安全 (重点) 4.1 观察线程不安全 static class Counter {public int count 0;void increase() {count;} } public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();Thread t1 new Thread(()…

VMware16.0安装教程和创建

许可证&#xff1a; ZF3R0-FHED2-M80TY-8QYGC-NPKYFYF390-0HF8P-M81RQ-2DXQE-M2UT6ZF71R-DMX85-08DQY-8YMNC-PPHV8设置网络 添加镜像 下载centos7镜像网址https://mirrors.aliyun.com/centos/7/isos/x86_64/?spma2c6h.25603864.0.0.d7724511YPrZpg win10镜像地址https://ww…

Linux+Docker+Gitlab+Jenkins+虚拟内存

最近想研究一下怎么自动化发布项目,于是找到了gitlab+jenkins这个组合,正好借机也研究一下最近很火的docker技术。本篇共分为五部分,分别为安装要求,内存虚拟化,安装docker,安装gitlab,安装jenkins。 一、 安装要求 1 Docker安装要求: 1.1 操作系统 Docker只支持64…

unittest单元测试

java的单元测试框架Junit和TestNG&#xff0c;python里面也有单元测试框架-unittest,相当于是一个python版的junit。python里面的单元测试框架除了unittest,还有一个pytest框架&#xff0c;但是用的比较少 unittest注意点&#xff1a; 导入unittest模块 类名的第一个字母大写&…

代码随想录算法学习心得 40 | 139. 单词拆分、背包问题总结...

一、单词拆分 链接&#xff1a;力扣 描述&#xff1a;给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 思路如下&…

【Linux | Shell】Linux 安全系统 —— 用户、组、文件权限 - 阅读笔记

目录 一、Linux 的安全性1.1 /etc/passwd 文件1.2 /etc/shadow 文件1.3 添加新用户 —— useradd1.4 删除用户 —— userdel1.5 修改用户 —— usermod、passwd、chpasswd 二、使用 Linux 组2.1 /etc/group 文件2.2 创建新组 —— groupadd2.3 修改组 —— groupmod 三、理解文…

Jenkins可持续集成Python自动化脚本

目录 前言 一、Jenkins搭建在Windows上 二、Jenkins搭建在Linux上 &#x1f381;更多干货 完整版文档下载方式&#xff1a; 本文讲解Jenkins如何每次定时的从SVN服务器上拉取最新的代码并执行本地库里的脚本 前言 1、本地代码库目录F:\5i5jautest内有测试文件all_tests.…

Attention,注意力机制

在机器视觉任务中&#xff0c;每一张图片都有重点区域&#xff0c;而非每一个像素对模型理解图片都同等重要。 在自然语言处理任务中&#xff0c;每一段文字都有重点词语&#xff0c;而非每一个字对模型理解语句都同等重要。 如此&#xff0c;在神经网络模型中引入注意力&#…

做投票小程序线上投票制作制作图片投票链接如何做投票小程序

小程序投票活动如何做&#xff1f;很多企业在运营当中&#xff0c;都会通过投票活动来进行推广&#xff0c;从而达到吸粉、增加用户粘度等效果。而此类投票活动&#xff0c;通过小程序就可以实现&#xff0c;操作简单。 我们现在要以“时尚新态度”为主题进行一次投票活动&…

WiFi 时钟+本地温度

[ WiFi 时钟 ] [ WiFi 天气时钟 ] [ WiFi 时钟本地温度 ] 夏天到了&#xff0c;显示器上放一个时钟&#xff0c;顺便实时测量本地室温&#xff0c;看看空调工作是否正常也算是个实用制作。 用到零件共 4 个&#xff1a; 400孔面包板 &#xff08; 大号…

docker配置nacos

1 拉取nacos镜像并启动 docker pull nacos/nacos-server 2 启动nacos命令 docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODEhostname -e MODEstandalone nacos/nacos-server 至此&#xff0c;我们已经可以使用nacos服务&#xff0c;UI地址:http://:8848/nacos …

计算机体系结构基础知识介绍之高级分支预测(二)

一、标记混合预测器 分支预测的目的是根据历史信息来预测分支指令的跳转方向和目标地址&#xff0c;从而提高流水线的效率。不同的分支预测方法有不同的优缺点&#xff0c;因此有人提出了一种将多种预测方法结合起来的方案&#xff0c;混合预测器。这种方案可以根据不同的分支…