JavaEE-进程与线程

news2024/10/13 4:16:24

1.进程

1.1什么是进程

每个应⽤程序运⾏于现代操作系统之上时,操作系统会提供⼀种抽象,好像系统上只有这个程序在运 ⾏,所有的硬件资源都被这个程序在使⽤。这种假象是通过抽象了⼀个进程的概念来完成的,进程可 以说是计算机科学中最重要和最成功的概念之⼀。
进程是操作系统对⼀个正在运⾏的程序的⼀种抽象,换⾔之,可以把进程看做程序的⼀次运⾏过程; 同时,在操作系统内部,进程⼜是操作系统进⾏资源分配的基本单位。

1.2进程控制块抽象(PCB Process Control Block)

计算机内部要管理任何现实事物,都需要将其抽象成⼀组有关联的、互为⼀体的数据。在 Java 语⾔ 中,我们可以通过类/对象来描述这⼀特征。
// 以下代码是 Java 代码的伪码形式,重在说明,⽆法直接运⾏
class PCB {
 // 进程的唯⼀标识 —— pid;
 // 进程关联的程序信息,例如哪个程序,加载到内存中的区域等
 // 分配给该资源使⽤的各个资源
 // 进度调度信息(留待下⾯讲解)
 }
这样,每⼀个 PCB 对象,就代表着⼀个实实在在运⾏着的程序,也就是进程。
操作系统再通过这种数据结构,例如线性表、搜索树等将 PCB 对象组织起来,⽅便管理时进⾏增删查 改的操作。

1.3CPU 分配⸺进程调度(Process Scheduling)

为了便于讨论和理解,我们⼤部分的场景下假设是单CPU单核的计算机。
操作系统对CPU资源的分配,采⽤的是时间模式⸺不同的进程在不同的时间段去使⽤ CPU 资源。

1.4内存分配⸺内存管理(Memory Manage)

操作系统对内存资源的分配,采⽤的是空间模式⸺不同进程使⽤内存中的不同区域,互相之间不会⼲扰。

1.5进程间通信(Inter Process Communication)

如上所述,进程是操作系统进⾏资源分配的最⼩单位,这意味着各个进程互相之间是⽆法感受到对⽅存在的,这就是操作系统抽象出进程这⼀概念的初衷,这样便带来了进程之间互相具备”隔离性
(Isolation)“。
但现代的应⽤,要完成⼀个复杂的业务需求,往往⽆法通过⼀个进程独⽴完成,总是需要进程和进程 进⾏配合地达到应⽤的⽬的,如此,进程之间就需要有进⾏“信息交换“的需求。进程间通信的需求 就应运⽽⽣。
⽬前,主流操作系统提供的进程通信机制有如下:
1. 管道
2. 共享内存
3. ⽂件
4. ⽹络
5. 信号量
6. 信号
其中,⽹络是⼀种相对特殊的 IPC 机制,它除了⽀持同主机两个进程间通信,还⽀持同⼀⽹络内部⾮同⼀主机上的进程间进⾏通信。

2.认识线程(Thread)

2.1概念

(1)线程是什么

⼀个线程就是⼀个 "执⾏流". 每个线程之间都可以按照顺序执⾏⾃⼰的代码. 多个线程之间 "同时" 执⾏着多份代码.
⼀家公司要去银⾏办理业务,既要进⾏财务转账,⼜要进⾏福利发放,还得进⾏缴社保。
如果只有张三⼀个会计就会忙不过来,耗费的时间特别⻓。为了让业务更快的办理好,张三⼜找来两 位同事李四、王五⼀起来帮助他,三个⼈分别负责⼀个事情,分别申请⼀个号码进⾏排队,⾃此就有了三个执⾏流共同完成任务,但本质上他们都是为了办理⼀家公司的业务。
此时,我们就把这种情况称为多线程,将⼀个⼤任务分解成不同⼩任务,交给不同执⾏流就分别排队执⾏。其中李四、王五都是张三叫来的,所以张三⼀般被称为主线程(Main Thread)。

(2)为啥要有线程

⾸先, "并发编程" 成为 "刚需".  

单核 CPU 的发展遇到了瓶颈. 要想提⾼算⼒, 就需要多核 CPU. ⽽并发编程能更充分利⽤多核 CPU资源.

有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发程.

其次, 虽然多进程也能实现 并发编程, 但是线程⽐进程更轻量. 

创建线程⽐创建进程更快.
销毁线程⽐销毁进程更快.
调度线程⽐调度进程更快.
最后, 线程虽然⽐进程轻量, 但是⼈们还不满⾜, 于是⼜有了 "线程池"(ThreadPool) 和 "协程"
(Coroutine)

(3)进程与线程的区别

进程是包含线程的. 每个进程⾄少有⼀个线程存在,即主线程。
进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.
⽐如,每个客⼾来银⾏办理各⾃的业务,但他们之间的票据肯定是不想让别⼈知道的,否则钱不就被其他⼈取⾛了么。⽽上⾯我们的公司业务中,张三、李四、王五虽然是不同的执⾏流,但因为办理的都是⼀家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最⼤区别。
进程是系统分配资源的最⼩单位,线程是系统调度的最⼩单位。
⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带⾛(整个进程崩溃).

(4) Java 的线程 和 操作系统线程 的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对⽤⼾层提供了⼀些 API 供⽤⼾使⽤(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进⾏了进⼀步的抽象和封装.

2.2 第⼀个多线程程序

感受多线程程序和普通程序的区别:
每个线程都是⼀个独⽴的执⾏流
多个线程之间是 "并发" 执⾏的.
import java.util.Random;

public class ThreadDemo {
 private static class MyThread extends Thread {
     @Override
     public void run() {
         Random random = new Random();
         while (true) {
         // 打印线程名称
             System.out.println(Thread.currentThread().getName());
             try {
                 // 随机停⽌运⾏ 0-9 秒
                 Thread.sleep(random.nextInt(10));
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
 }

     public static void main(String[] args) {
         MyThread t1 = new MyThread();
         t1.start();
         Random random = new Random();
         while (true) {
             // 打印线程名称
             System.out.println(Thread.currentThread().getName());
             try {
                 Thread.sleep(random.nextInt(10));
             } catch (InterruptedException e) {
                 // 随机停⽌运⾏ 0-9 秒
                 e.printStackTrace();
             }
         }
     }

}
使⽤ jconsole 命令观察线程

 

2.3 创建线程

⽅法1 继承 Thread 类

继承 Thread 来创建⼀个线程类.
class MyThread extends Thread {
     @Override
     public void run() {
         System.out.println("这⾥是线程运⾏的代码");
     }
}
创建 MyThread 类的实例
 MyThread t = new MyThread();

 调⽤ start ⽅法启动线程

 t.start(); // 线程开始运⾏

 ⽅法2 实现 Runnable 接⼝

1. 实现 Runnable 接⼝
class MyRunnable implements Runnable {
     @Override
     public void run() {
         System.out.println("这⾥是线程运⾏的代码");
     }
}
2. 创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为 target 参数.
Thread t = new Thread(new MyRunnable());

3. 调⽤ start ⽅法

t.start(); // 线程开始运⾏
对⽐上⾯两种⽅法:
继承 Thread 类, 直接使⽤ this 就表⽰当前线程对象的引⽤.
实现 Runnable 接⼝, this 表⽰的是 MyRunnable 的引⽤. 需要使⽤Thread.currentThread()

其他变形

匿名内部类创建 Thread ⼦类对象
// 使⽤匿名类创建 Thread ⼦类对象
Thread t1 = new Thread() {
     @Override
     public void run() {
         System.out.println("使⽤匿名类创建 Thread ⼦类对象");
     }
};
匿名内部类创建 Runnable ⼦类对象
// 使⽤匿名类创建 Runnable ⼦类对象
Thread t2 = new Thread(new Runnable() {
     @Override
     public void run() {
         System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
     }
});
lambda 表达式创建 Runnable ⼦类对象
// 使⽤ lambda 表达式创建 Runnable ⼦类对象
Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
Thread t4 = new Thread(() -> {
     System.out.println("使⽤匿名类创建 Thread ⼦类对象");
});

2.4 多线程的优势-增加运⾏速度

可以观察多线程在⼀些场合下是可以提⾼程序的整体运⾏效率的。
使⽤ System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.
serial 串⾏的完成⼀系列运算. concurrency 使⽤两个线程并⾏的完成同样的运算.
public class ThreadAdvantage {
     // 多线程并不⼀定就能提⾼速度,可以观察,count 不同,实际的运⾏效果也是不同的
     private static final long count = 10_0000_0000;

     public static void main(String[] args) throws InterruptedException {
         // 使⽤并发⽅式
         concurrency();
         // 使⽤串⾏⽅式
         serial();
     }

     private static void concurrency() throws InterruptedException {
         long begin = System.nanoTime();
 
         // 利⽤⼀个线程计算 a 的值
         Thread thread = new Thread(new Runnable() {
             @Override
             public void run() {
                 int a = 0;
                 for (long i = 0; i < count; i++) {
                     a--;
                 }
             }
         });

         thread.start();
         // 主线程内计算 b 的值
         int b = 0;
         for (long i = 0; i < count; i++) {
             b--;
         }
         // 等待 thread 线程运⾏结束
         thread.join();
 
         // 统计耗时
         long end = System.nanoTime();
         double ms = (end - begin) * 1.0 / 1000 / 1000;
         System.out.printf("并发: %f 毫秒%n", ms);
     }


     private static void serial() {
         // 全部在主线程内计算 a、b 的值
         long begin = System.nanoTime();
         int a = 0;
         for (long i = 0; i < count; i++) {
             a--;
         }
         int b = 0;
         for (long i = 0; i < count; i++) {
             b--;
         }
         long end = System.nanoTime();
         double ms = (end - begin) * 1.0 / 1000 / 1000;
         System.out.printf("串⾏: %f 毫秒%n", ms);
     }
}
并发: 399.651856 毫秒
串⾏: 720.616911 毫秒

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

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

相关文章

Arduino IDE 导入库(DallasTemperature.h和OneWire.h)

1打开IDE 2点击项目 3点击导入库 4在点击管理库 5搜索库 在下图的位置输入库名即可 6安装即可

基于Springboot+Vue的医疗废物管理系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在系统…

基于yolov8、yolov5的交通标志检测识别系统(含UI界面、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

避免 Python 类型转换错误:实用指南

在 Python 中&#xff0c;变量的类型是明确的&#xff0c;不能在没有显式转换的情况下直接将一种类型的值赋给另一种不同类型的变量。例如&#xff0c;以下将字符串与整数直接相加会报错&#xff0c;这跟其它语言有所差异&#xff0c;其它语言会进行隐式转 a "5" b…

c++类与对象三

C类与对象三 上期我们介绍了类的实例化&#xff0c;大小计算&#xff0c;还有this指针。这期我们继续深入更高层次的用法 类的六个默认函数 如个一个类里面没有成员&#xff0c;就是空类&#xff0c;但是空类里面真的什么都没有吗&#xff0c;并不是&#xff0c;在编译器中&…

大数据新视界 --大数据大厂之大数据环境下的零信任安全架构:构建可靠防护体系

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Javascript 仅允许在异步函数和模块顶级使用 “await“

这个错误的原因&#xff0c;是我们在一个普通函数里调用了async函数&#xff0c;因为async 函数是异步函数&#xff0c;它返回一个 Promis&#xff0c;await必须与async一同出现。所以这里普通函数无法调用它。 async和await只是使函数调用看起来像同步而已&#xff0c;比如下…

【MMMLP】核心方法解读

此方法用于顺序推荐&#xff0c;和我的研究方向不一样&#xff0c;所以这里只探讨值得借鉴的部分 abstract&#xff1a; 现有的顺序推荐方法要么不能直接处理多模态&#xff0c;要么计算量大。为了解决这个问题&#xff0c;我们提出了一种新的多模态多层感知器&#xff08;MM…

MYSQL-windows安装配置两个或多个版本MYSQL

安装第一个mysql很简单&#xff0c;这里不再赘述。主要说说第二个怎么安装&#xff0c;服务怎么配置。 1. 从官网下载第二个MySQL并安装 一般都是免安装版了&#xff0c;下载解压到某个文件目录下(路径中尽量不要带空格或中文)&#xff0c;再新建一个my.ini文件&#xff08;或…

QGroundControl最新版本MacOS平台编译(使用CMakeLists.txt)

1.下载源码: git clone https://github.com/mavlink/qgroundcontrol.git --recursive 2.安装依赖: brew install GStreamer 设置环境变量:GST_PLUGIN_PATH 安装SDL2: brew install SDL2

C#自定义特性

特性的用处 一般用来影响某一个类的个别字段或者方法 定义特性 需要将类继承Attribute 可以通过构造函数的方式影响使用特性的方法 可以通过给自定义的特性通过加AttributeUsage特性的方法进行进一步管理 AttributeUsage特性默认传三个参数 第一个参数一般用来约束此自定义…

怎么把m4a转换成mp3?8种关于m4a转成MP3格式的转换方法

怎么把m4a转换成mp3&#xff1f;尽管m4a格式在音质上表现突出&#xff0c;但并不是所有设备和软件都能支持&#xff0c;给一些用户带来了不便。为了保证音乐文件能够在更多设备和平台上播放&#xff0c;许多人选择将m4a转换为MP3格式。MP3几乎可以在所有播放器和设备上使用&…

LeetCode讲解篇之2320. 统计放置房子的方式数

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们首先发现一个规律街道两侧是否放置房子是独立的&#xff0c;即放置房子的方式数 一侧放置房子的方式数 * 另一侧放置房子的方案数 一侧放置房子的方式数的二次方 对于一侧[0, i]范围内地块放置房子的方式…

starrocks-删除表字段

1、背景 之前做了个大宽表&#xff0c;将近100个字段&#xff0c;但是后来发现很多字段在实际生产上都没有用到&#xff0c;并且随着数据量的增加&#xff0c;给集群的存储以及消费任务的解析带来了比较大的压力。所以决定对字段做删除处理。 当前的表是使用routine load任务从…

渗透测试 之 AD域渗透 【AS-REP Roasting】 攻击技术详解

说明: AS-REP Roasting是一种对用户账户进行离线爆破的攻击方式。但是该攻击方式使用比较受限&#xff0c;因为其需要用户账户设置“不要求Kerberos预身份验证”选项&#xff0c;而该选项默是没有勾选的。Kerberos 预身份验证发生在Kerberos身份验证的第一阶段&#xff08;AS_…

14. 最长公共前缀【字符串】

文章目录 14. 最长公共前缀解题思路Go代码 14. 最长公共前缀 14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs ["flower","flow&…

STM32 QSPI接口驱动GD/W25Qxx配置简要

STM32 QSPI接口GD/W25Qxx配置简要 &#x1f4dd;本篇会具体涉及介绍Winbond&#xff08;华邦&#xff09;和GD(兆易创新) NOR flash相关型号指令差异。由于网络上可以搜索到很多相关QSPI相关知识内容&#xff0c;不对QSPI通讯协议做深度解析。 &#x1f516;首先确保所使用的ST…

2022年10月自考《数据库系统原理》04735试题

目录 一.单选题 二.填空题 三.设计题 四.简答题 五.综合题 一.单选题 1.数据库系统管理员的英文缩写是 (书中)P29页 A.Data B.DB C.DBA D.DBS 2.客户/服务器模式中&#xff0c;客户端和服务器可以同时工作在同一台计算机上&#xff0c;该方式称为 (书中)P37页 A.单机方…

使用Copilot自动在Rstudio中写代码,提高效率!

原文链接&#xff1a;使用Copilot自动在Rstudio中写代码&#xff0c;提高效率&#xff01; 2022年教程总汇 2023年教程总汇 引言 今天我们分享&#xff0c;在Rstuido中使用copilot自动写代码&#xff0c;提高你的分析和绘图效率。 copilot是2024年9月后引入到Rstuido中&…

如何在 IDEA 中导入 Java 项目的 Git 仓库并启动

目录 前言1. 从 Git 仓库导入 Java 项目2. 配置 Maven2.1 配置 Maven 仓库和设置文件2.2 加载依赖 3. 配置 Tomcat 并运行项目3.1 配置 Tomcat3.2 配置 Server URL3.3 启动项目 4. 常见问题与解决方法4.1 Maven 依赖无法下载4.2 Tomcat 部署失败4.3 项目启动后无法访问 结语 前…