JAVA中的线程池说明一

news2024/9/22 17:30:20

 系列文章

JAVA中的线程池说明一

JAVA中的线程池说明二

目录

1.为什么需要线程池?

2.什么是线程池?

3.标准库中的线程池

4.实现自定义线程池


1.为什么需要线程池?

       线程的存在意义在于解决并发编程中进程开销过大的问题,因此引入了线程,也被称为"轻量级线程"。相比于创建进程,创建线程更加高效;同样地,销毁线程比销毁进程更高效,调度线程比调度进程更高效。

       在许多情况下,使用多线程可以替代进程来实现并发编程。然而,随着并发程度的提高和对性能要求的提高,频繁创建和销毁线程会导致线程创建变得不那么轻量。为了降低创建和销毁线程的开销,我们可以使用线程池来管理线程资源。

2.什么是线程池?

       线程池是一种管理线程的机制,它预先创建一定数量的线程,并将它们放入一个池中。当需要执行任务时,线程池会从池中选择一个空闲的线程来执行任务,而不是每次都创建新的线程。一旦任务完成,线程会被归还到线程池中,等待下一次使用。

       在之前我们也了解过,相关的池化技术,字符串常量池,数据库连接池,HTTP连接池等,池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。

线程池的主要优点包括:

  • 提高性能,降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程池可以限制同时运行的线程数量,避免过多的线程导致系统资源耗尽,使用线程池可以进行统一的分配,调优和监控。

上面我们说到:

将线程从线程池里获取,用完后还给线程池,这两个动作比创建/销毁进程更高效

那这里的原因是什么呢?

       是因为创建/销毁进程是交由操作系统内核完成的,从线程从线程池里获取,用完后还给线程池,是我们自己用户的代码就可以实现的,不必交给内核操作。

那什么是操作系统内核呢?

       操作系统内核是操作系统的核心部分,它负责管理系统的资源、调度进程和线程的执行、处理硬件设备等。它是操作系统与计算机硬件之间的接口,提供了系统级别的服务和功能。

       比如在银行大厅里,用户们可以自由地操作,这就像计算机程序中的"用户态"。在用户态下,执行的是程序员编写的代码,用户可以按照自己的意愿进行各种操作,完全由程序员来决定。

       然而,有些特定的任务必须在银行柜台内完成,客户无法直接进入柜台,必须通过银行工作人员来执行这些任务。这与计算机程序中的"内核态"相似。内核态的操作都在操作系统的内核中进行,内核提供给程序员一些应用程序接口(API),称为系统调用。程序员可以通过调用这些API来驱动内核完成某些工作。系统调用的内容直接与内核的代码相关,这部分代码不受程序员的控制,完全由内核自行处理。

       与内核相比,用户态的程序行为是可控的。如果用户想要立即完成某项工作,可以直接在用户态下进行。但是,如果想要通过内核从系统中创建一个新的线程,就需要使用系统调用,让内核来执行这一操作。然而,我们无法确定内核当前正在处理多少其他任务。例如,如果你希望柜台工作人员帮你存钱,但前面可能有很多人正在排队等待服务。因此,当我们使用系统调用来执行内核代码时,无法确切知道内核将执行哪些具体操作,这使得整个过程相对"不可控"。

3.标准库中的线程池

       在Java标准库中,也有现成的线程池,可以直接进行使用,下面这个代码是指,创建一个线程池,池子里现成数目固定为10个。

    public static void main(String[] args) {
        //创建一个线程池,池子里现成数目固定为10个
        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);
                }
            });
        }
    }

ExecutorService pool = Executors.newFixedThreadPool(10);

       在这个例子中,我们使用了`newFixedThreadPool()`方法创建了一个固定大小的线程池。这种设计模式被称为"工厂模式"。

       线程池提供了一个重要的方法`submit`,可以用来向线程池提交多个任务。当我们运行这段代码时,会发现主线程已经结束,但是线程池中的线程仍然在运行,这是因为这些线程都是前台线程,它们会阻止线程池的关闭。

       需要注意的是,这里我们向线程池提交了1000个任务,这些任务将被10个线程平均分配。每个线程大约执行100个任务,但由于每个任务执行时间相近,因此每个线程处理的任务数量也大致相同。可以认为这1000个任务被放入一个队列中,10个线程依次从队列中取出任务并执行。

int n = i;

       这里需要再定义一个变量n,而不是直接使用i的原因是变量捕获。

       在主线程中,i是一个局部变量,当主线程的for循环结束时,i将被销毁。然而,有可能主线程的for循环结束,但当前的run任务尚未在线程池中执行。

       为了避免作用域差异导致后续执行run方法时i已经销毁,我们需要进行变量捕获,即将主线程中的i值传递给当前run方法所在的线程。这样,即使主线程的i被销毁,run方法仍然可以访问到正确的值。

       在Java中,Lambda表达式和匿名内部类都可以捕获外部作用域中的变量。对于JDK 1.8之前的版本,只能捕获被final修饰的局部变量或者实例变量。这是因为这些变量一旦初始化后就不能改变,所以它们可以在Lambda表达式或匿名内部类中使用而不会引发线程安全问题。

       然而,从JDK 1.8开始,Java引入了一个新的特性叫做"effectively final"。这个特性允许我们在Lambda表达式或匿名内部类中捕获那些实际上没有被修改过的变量。换句话说,即使变量没有被明确地声明为final,只要它在Lambda表达式或匿名内部类中没有被修改过,就可以被捕获。

4.实现自定义线程池

这里的实现自定义线程池比较简单,不多过于赘述。

class MyThreadPool {
    private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();

    //n表示线程数量
    public MyThreadPool(int n) {
        //在这里创建线程
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }

    //注册任务给线程池
    public void submit(Runnable runnable) {
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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

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

相关文章

【Hot100】LeetCode—74. 搜索二维矩阵

原题链接&#xff1a; 74. 搜索二维矩阵 1- 思路 二分 ① 实现一个二分函数② 对每行数组进行二分 2- 实现 ⭐74. 搜索二维矩阵——题解思路 class Solution {public boolean searchMatrix(int[][] matrix, int target) {for(int[] m:matrix){if(binarySearch(m,target)){re…

【知识图谱】3、Python操作图数据库neo4j示例

今天突然想起上次知识图谱系列埋了一个坑&#xff08;【知识图谱】1、Neo4j环境搭建入门指南:从零开始玩转图数据库&#xff09;&#xff0c;说后续写一篇关于Python操作neo4j的示例。趁着周六有充足时间&#xff0c;这里写个demo补上。 本文demo还是以面试的求职者、岗位要求…

浅谈-Unity内存管理

灵魂拷问-什么是内存 物理内存虚拟内存内存寻址方位 物理内存 下面是一张i7的处理器的芯片细节图&#xff0c;在整个板载面积上我们可以很明显的看到Shared L3 Cache占用了最大面积。为什么&#xff1f;因为硬件产商为了让我们忽略掉CPU访问内存是一个非常慢速的过程&#x…

Nginx: TCP建立连接的优化和启用Fast Open功能

TCP 建立连接优化 在三次握手中&#xff0c;相关TCP的内核参数可优化这一过程 net.ipv4.tcp_syn_retries 6net.ipv4.tcp_synack_retries 5net.ipv4.tcp_syncookies 0net.ipv4.tcp_max_syn_backlognet.core.somaxconnnet.core.netdev_max_backlog 1 &#xff09; net.ipv4…

游戏:科技强国的璀璨星芒与经济增长新动力

游戏&#xff1a;科技强国的璀璨星芒与经济增长新动力 在时代的浪潮中&#xff0c;游戏正以一种令人瞩目的姿态&#xff0c;成为科技强国之路上一颗闪耀的星&#xff0c;同时也对经济有着多方面的深远影响。 从《黑神话&#xff1a;悟空》的爆火&#xff0c;到美国、英国、法国…

磐石云AXB小号平台同时支持AXYB、AXN、AXYBN

外卖订单&#xff0c;物流配送&#xff0c;金融&#xff0c;房地产&#xff0c;等行业都在使用订单小号或者说是工作号。 在当今数字化信息爆炸的时代&#xff0c;通信方式的多样化和复杂化给我们带来了便利&#xff0c;但也带来了管理的挑战。面对繁杂的通信需求&#xff0c;…

Ai Illustrator 取消吸附到像素点,鼠标拖动的时候只能到像素点

Ai Illustrator 取消吸附到像素点&#xff0c;鼠标拖动的时候只能到像素点 在做图的时候无意间变成吸附到像素点了&#xff0c;导致无法更细致的移动点。 像这样&#xff1a; 关闭的方法是打开上面菜单中的 【视图】取消勾选【对齐像素】 即可。 结果就是&#xff1a;

C++实现彩虹猫时空隧道特效(无害)

#include <Windows.h> // 如果不是在Visual Studio环境下运行的话W最好改小写。 using namespace std;int main() {for (int i 1; i < 10; i) {HDC hdc GetWindowDC(GetDesktopWindow());RECT rect;GetWindowRect(GetDesktopWindow(), &rect);StretchBlt(hdc, r…

240901-通过端口转发在局域网内访问WLS2中Gradio的Web应用

A. 需求描述 两台不同的电脑共用同一个路由器&#xff0c;其中一台电脑时Windows&#xff0c;上面安装了WSL2&#xff0c;我在WLS2中启动了一个Gradio的网络应用&#xff0c;并设置了server_name‘0.0.0.0’&#xff0c;另外一台电脑如何访问该网络应用。 B. 方法介绍 要使另一…

STM32H750+CubeIDE+FreeRTOS+ETH(LAN8720A)+LWIP

文章目录 STM32H750CubeIDEFreeRTOSETH(LAN8720A)LWIPCubeIDE配置RCC时钟树SYSETH串口MPUFreeRTOSLWIPGPIO然后就可以点击生成代码了&#xff01; 代码修改printf重定向补充硬件复位更改补充链接文件然后就可以编译下载ping成功了&#xff01; socket网络编程 STM32H750CubeIDE…

Freepik发布号称目前最强AI图像生成器Mystic

Freepik 于2024年8月27日正式推出了新的人工智能图像模型 “Mystic”&#xff0c;并号称是目前最强的AI图像生成器&#xff0c;本文从介绍Mystic开始&#xff0c;剖析其技术细节&#xff0c;功能特色&#xff0c;收费价格&#xff0c;并与当前市场上领先的 Midjourney V6.1 进行…

【dotnet】vscode配置dotnet开发环境

下载dotnet sdk https://dotnet.microsoft.com/en-us/download/visual-studio-sdks下载完安装&#xff0c;安装完毕在cmd中输入以下代码 dotnet出现以下结果代表安装完毕 在vscode终端中输入以下创建web项目 dotnet new webapp --output aspnetcoreapp --no-https上述命令…

[Jsprit]Jsprit学习笔记-vrp问题的求解

目录 一、整体的求解逻辑主要步骤 二、搜索策略的选择三、搜索策略执行解1、解的选择2、解的破坏3、解的接受3.1 新解的接受策略 一、整体的求解逻辑 下面是Jsprit实现的代码部分 public Collection<VehicleRoutingProblemSolution> searchSolutions() {logger.info(&q…

基于3D Slicer与matlab平台的图像引导介入手术-demo

1.实现手术手术导航的基本框架 2、基本协议框架 3、演示视频 DemoWithChinse2

unordered系列容器的实现

1. unordered_set与unordered_map的结构 我们知道STL中的unordered_set与unordered_map底层就是一个开散列的哈希表 1.1 unordered_set的结构 我们知道unordered_set其实就是K模型&#xff0c;所以unordered_set容器对红黑树的封装如下&#xff1a; template<class k, cl…

VTK随笔十二:体绘制(体绘制管线、vtkVolumeMapper、vtkVolume、不规则网格数据体绘制技术 )

体绘制&#xff0c;有时又称作三维重建(区别于投影图像的三维重建)&#xff0c;是一种直接利用体数据来生成二维图像的绘制技术。与面绘制不同&#xff0c;体绘制技术不需要提取体数据内部的等值面&#xff0c;它是一个对三维体数据进行采样和合成的过程。体绘制能够通过设置不…

【深度学习入门】计算机视觉任务

一、引言 对于神经网络&#xff0c;可以把中间的隐藏层看作一个黑盒子&#xff0c;这个黑盒子能自动选择如何提取特征&#xff0c;这不同于传统机器学习的人工操作&#xff0c;它的实现原理也是我们学习深度学习的重点。本文章以计算机视觉任务中的图像分类任务为例子&#xff…

zookeeper 集群搭建 及启动关闭脚本

1准备奇数台机子3&#xff0c;5&#xff0c;7 我准备的是三台 192.168.58.81 zookeeper-1 192.168.58.82 zookeeper-2 192.168.58.83 zookeeper-3 下载jdk 把他配置环境变量并检查是否是环境变量 echo $JAVA_HOME cd /opt/software wget http://mirrors.hust.edu.…

docker部署project-exam-system项目

8月30日笔记 项目实战&#xff1a;使用docker部署project-exam-system 1、背景&#xff1a; 使用基础的docker指令来创建镜像&#xff0c;实现项目的发布&#xff0c;使用Dockderfile&#xff0c;docker compose编排容器。 2、环境准备&#xff1a; &#xff08;1&#x…

sts 0/1 没有 pod生成 -> kube-controller-manager没了

kube-controller-manager.yaml 在 nerdctl ps -a 看不到 journalctl -xu kubelet > /tmp/kubelet.log /tmp/kubelet.log 老6