java并发编程笔记 之 线程和进程

news2024/11/15 17:37:40

文章目录

  • 前言
  • 线程
    • 线程优先级和时间片
    • 创建多线程及运行
    • 线程的状态
  • 进程
    • 查看进程的命令
    • 进程的通信方式
  • 线程和进程的区别
    • 从关系上
    • 疑问集锦

前言

  • 并发

    1、并发是指在同一时间段内,计算机系统能够处理多个任务的能力。
    2、在并发编程中,我们可以理解为多个线程竞争同一个资源
    3、操作系统中有 线程控制块进程控制块,用来记录当前时间片执行到的程序位置

  • 并行

    多个线程或者进程各自执行各自的任务,互不干扰。
    (并行不会出现竞争同一个资源,在此我们不多做讨论,后续文章均已并发为主)

  • 高速缓存

    对于频繁重复访问的数据,我们可以存储在高速缓存中,这样可以释放总线,提高计算速度
    高速缓存的容量比较小,当容量不足时会往内存中写回数据(具体的写回时间不确定)。

线程

  • 定义:
    • 是一个指令流(指令和数据的不断入栈和出栈),即程序的执行顺序,是进程中真正执行的基本单位
  • 单线程:
    • 只有一个虚拟机栈,程序从上到下依次执行
    • 单线程是作为局部变量存在的
  • 多线程:
    • 1、多个指令流,相当于创建多个虚拟机栈
    • 2、继承Thread后不再是局部变量,变成一个线程

线程优先级和时间片

  • 和操作人员接触多的指令,它的优先级会更高(即更容易获得操作系统的时间片)
  • 如果时间片结束时未完成所有任务,则记录执行到的位置,然后任务重新会进入就绪态
  • 如果时间片未到时间但已完成任务,则剩余的时间片会执行其他任务

创建多线程及运行

  • 线程代码:

    public class Test {
        public static void main(String[] args) {
            int[] arr = {0};
            Thread threadA = new Thread(){
                @Override
                public void run() {
                    System.out.println("theadA启动 ~~ ");
                    for(int i=0;i<1000000;i++){
                        arr[0]++;
                    }
                    System.out.println("threadA下的arr[0] = " + arr[0]);
                }
            };
    
            Thread threadB = new Thread(){
                @Override
                public void run() {
                    System.out.println("theadB启动 ~~ ");
                    for (int i = 0; i < 1000000; i++) {
                        arr[0]++;
                    }
                    System.out.println("threadB下的arr[0] = " + arr[0]);
                }
            };
    
            threadA.start();	// threadA进入线程就绪队列
            threadB.start();	// threadB进入线程就绪队列
            System.out.println(arr[0]);
        }
    }
    
  • 代码说明:

    1、线程.start()是进入线程就绪队列,新建线程后需要启用start进入就绪队列
    2、当前有三个线程:threadA、threadB 和main主线程

  • 运行结果(打印结果是不确定的):

    第一种可能结果:
    在这里插入图片描述
    第二种可能结果:
    在这里插入图片描述
    第三种可能结果:
    在这里插入图片描述
    第四种可能结果:
    在这里插入图片描述

  • 结果分析:

    • 1、threadA和threadB是互相独立的,执行顺序是不确定的。

      线程内部的代码时按顺序来执行的

    • 2、主线程main优先执行的概率较大(注意只是概率哦)
    • 3、先进入就绪队列(如threadA)的先执行的概率大

      可以观察到上边几种结果,threadA先打印的次数比threadB要多

    • 4、线程被选中后,分配的时间片时长是不固定的

      我们以 结果一结果三 来进行分析:
      (1)线程threadA被选中,分配了时间片运行:打印开始启动后对arr[0]进行增加
      (2)arr[0]增到到”35932“后,时间片结束,开始运行 主线程main ,打印”35932“
      (3)仅 System.out.println(arr[0]) 是用不完分配给主线程main的时间片的,分配给主线程main的时间片用不完,当打印完后,就会结束主线程main继续执行下一个,要么选中threadA要么选中threadB
      (4)【选中threadB】,分配时间片:打印”启动“后对arr[0]进行增加
      (5)这里需要循环100w次,注意这里threadA和threadB中arr[0]增加是threadA和threadB交替着来的
      (6)当循环都完成的时候,打印出每个线程下的结果:可能先是threadA也可能先是threadB

  • 线程不安全:

    如果线程安全:无论哪个线程,最后打印的结果应该是200w
    (但实际结果并不这样)

    从严格物理上来讲,同一时刻只有一个线程指令通过总线来操作内存。
    而内存同一时刻也只能由一个线程来操作。

    因为有高速缓存
    (1)当threadA读取到arr[0]=0,然后开始计算
    (2)threadA操作的数据存储在高速缓存中,没有往回更新时,时间片结束
    (3)threadB读取arr[0],此时arr[0]=0,然后开始计算
    (4)当threadA计算到50w时,高速缓存更新arr[0],arr[0] = 50w
    (5)然后threadB计算到100w时,高速缓存更新arr[0],直接覆盖arr[0] = 100w
    (6)threadA再次读取到arr[0]=100w,执行剩下的50w次,最后计算结果arr[0]=150w,而不是200w
    (7)这里有两个极限:最小是2,最大是200w

线程的状态

  • 新建

    新建的线程并不会进入就绪态,需要启动

  • 就绪态

    进入就绪队列的顺序是有先后的,但是执行的时候不会按照进入的顺序来。
    即操作系统选中哪个线程先执行是不确定的
    先进入就绪态先执行的概率会大,但是不是绝对的

  • 运行态:被cpu选中

    1、运行中,当前时间片内未完成线程任务,当时间片结束时,通过线程控制块记录数据,线程重新进入就绪态
    2、在时间片内运行结束,进入死亡态

  • 死亡态

    线程运行完毕

  • 阻塞态:线程竞争加锁的资源失败,会进入阻塞队列

    当竞争的资源锁一旦被释放后,该线程会重新进入就绪队列参与竞争

  • 等待态:

    运行中的线程可以自己进入等待队列。
    进入等待队列中的线程,如果没有通知,则会一直留在队列,操作系统不会选中该任务

  • 睡眠态:

    此处不细讲,以下为两个进入睡眠态的方法:

    • Thread.sleep(时长) 方法进入休眠的线程不会释放持有的锁
    • object.wait()方法进入休眠的线程会释放锁,但是唤醒需要其他线程调用了该对象的 notify() 或 notifyAll() 方法

进程

  • 正在运行的程序,我们称为进程。
    (如我们打开的chrome浏览器,QQ音乐,微信,均被称为进程)
  • QQ音乐这个线程包含了代码、账号信息登数据以及内存空间
  • 如果我们选择了一个音乐播放,那么这是一个线程
  • 如果我们让播放器显示桌面歌词,这也是一个线程
  • 喜欢一个音乐,点击收藏,这也是一个线程

查看进程的命令

  • windows

    查看所有的进程:tasklist
    杀死进程:taskkill /F /PID pid号

  • linux

    查看所有的进程:ps -ef
    查看指定的进程:
    杀死进程:kill -9 pid号

进程的通信方式

  • 管道:

    原理:内核中的一个缓存。是一种半双工的通信方式,即一个进程的输出作为另一个进程的输入
    (单工:一方只能发送,其他只能接受,做不到信息互动。可以想象一下收音机)
    (半双工:需要等一方发送完毕,另一方才可以发送。可以想象一下对讲机)
    (双工:可以等一方发送完毕,也可以同时发送。可以想象一下升级的电话通话)

    优点:最简单
    缺点:效率最差,不适合进程间频繁的交换数据

  • FIFO:

    与管道类似,但是允许不想关的进程进行通信,且可以跨终端会话

  • 消息队列:

    允许进程以消息的形式进行通信,消息可以按照一定的顺序进行传递

    优点:可以边发送边接收,不需要等待完整的数据
    缺点:
    (1)每个消息体有最大长度的限制,队列所包含消息体的总长度也有上限
    (2)消息队列通信存在用户态和内核态之间的数据拷贝问题,耗性能

  • 共享内存区

    原理:不同的进程拿出一块虚拟内存空间,映射到相同的物理内存空间。这样一个进程写入的东西,另一个进程马上就能够看到,不需要进行拷贝。

    优点:解决了消息队列存在
    缺点:如果有多个进程网内存写入数据,后写数据的会对前边的数据有覆盖

  • 信号量

    • 原理

      基于共享内存的多进程不安全的情况,增加一个保护机制-信号量。
      信号量本质上是一个整型的计数器,用于实现进程间的互斥和同步。

    • 过程:信号量S初始为1;P操作是让信号量-1;V操作是让信号量+1
      在这里插入图片描述

    1、进程A访问共享内存前执行P操作,更改信号量:S=S-1=0
    2、进程A访问共享内存
    3、进程B访问共享内存前执行P操作,更改信号量:S=S-1=0-1=-1,临界资源被占用
    4、进程B被阻塞,进入阻塞队列
    5、进程C访问共享内存前执行P操作,更改信号量:S=S-1=-1-1=-2,临界资源被占用
    6、进程C被阻塞,进入阻塞队列
    7、进程A访问完毕,执行V操作,更改信号量:S=S+1=-2+1=-1
    8、唤醒阻塞队列中的进程B
    在这里插入图片描述
    信号量S=-1
    1、进程B访问共享内存
    2、进程B访问完毕,执行V操作,更改信号量:S=S+1=0
    3、唤醒阻塞队列中的进程C
    在这里插入图片描述
    1、进程C访问共享内存
    2、进程B访问完毕,执行V操作,更改信号量:S=S+1=1
    3、结束

  • 信号量互斥:当多进程访问共享内存时,信号量<1时不允许其他进程访问;

  • 信号量同步:当信号量恢复成初始值1时会唤醒其他进程来访问数据

线程和进程的区别

从关系上

1、进程是操作系统资源分配的基本单位,线程是程序执行的最小单位
2、每个进程都至少拥有一个线程来执行地址空间中的代码
3、进程是线程的容器,单个进程可以包含若干个线程,且这些线程可以同时执行进程地址空间中的代码
4、进程是安全的;多线程时会出现争抢同一个资源的情况,即多线程更改同一变量会出现问题,故线程是不安全的
5、多个线程共享进程的堆和方法区,但是每个线程都有自己的程序计数器、虚拟机栈和本地方法栈。

疑问集锦

  • 线程为什么不能独立存在

    因为线程是依赖于进程存在的。线程是进程内的执行单元,自己本身并不拥有系统资源,线程使用本身的资源
    线程如果允许独立存在,系统需要为它分配资源和空间,那么起始就等同于一个进程了。而线程是进程的轻量级,可以快速创建和销毁。

  • 线程之间为什么会交替运行

    因为有时间片轮转
    (分时操作系统中,windows的时间片分配不是固定时间,linux中时间片分配时固定的时间)

  • 线程安全是什么?

    同一个进程内的资源是共享给若干个线程的,当多个线程更改同一块内存区域时,会导致该内存的数据互相覆盖

  • 怎么保证线程安全?

    同步:使用synchronize关键字 或 reentrantlock
    不可变对象:创建值无法更改的对象,如增加关键字final
    原子类:使用atomicinteger等原子类执行原子操作
    volatile关键字:确保变量在多线程间可见
    内存屏障:通过thread.memorybarrier()方法强制执行内存访问顺序

  • 为什么进程开销比线程大

    进程之间资源不共享,每个进程都有自己独立的地址空间和系统资源,占用的内存大;线程之间资源共享,同一个进程中的线程共享进程的地址空间和系统资源,占用的内存小。

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

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

相关文章

chapter17-多线程基础——(自定义泛型)——day20

580-程序进程线程 581-并发并行 并发和并行也可以同时进行 582-继承Thread创建线程 583-多线程机制 主线程和子线程交替执行 单核&#xff1a;两个线程并发 多核&#xff1a;两个线程并行 主线程结束&#xff0c;不是说进程就结束&#xff0c;进程要等所有线程结束 584-为什…

模型Alignment之RLHF与DPO

1. RLHF (Reinforcement Learning from Human Feedback) RLHF 是一种通过人类反馈来强化学习的训练方法&#xff0c;它能够让语言模型更好地理解和执行人类指令。 RLHF 的三个阶段 RLHF 的训练过程一般分为三个阶段&#xff1a; 监督微调&#xff08;Supervised Fine-Tuning,…

认知杂谈82《跳出信息茧房,持续精进》

内容摘要&#xff1a; 互联网时代&#xff0c;信息丰富&#xff0c;但便捷性削弱了我们的好奇心。互联网是双刃剑&#xff0c;快速获取知识的同时&#xff0c;也让我们陷入“信息茧房”&#xff0c;限制视野。 好奇心减少&#xff0c;部分原因是互联网的“懒惰效应”&#xff0…

国家标准和团体标准有什么区别?

国家标准和团体标准的区别主要体现在以下几个方面&#xff1a; 1. 制定标准的主体不同&#xff1a;国家标准是由国家机构通过并公开发布的标准&#xff1b;团体标准是由学会、协会、商会、联合会、产业技术联盟等社会团体协调相关市场主体共同制…

【项目实战】如何在项目中基于 Spring Boot Starter 开发简单的 SDK

什么是SDK 通常在分布式项目中&#xff0c;类和方法是不能跨模块使用的。为了方便开发者的调用&#xff0c;我们需要开发一个简单易用的SDK&#xff0c;使开发者只需关注调用哪些接口、传递哪些参数&#xff0c;就像调用自己编写的代码一样简单。实际上&#xff0c;RPC(远程过…

element下拉框联动 或 多选 回显数据后页面操作不生效问题解决

第一种:多选回显不生效 解决方式: 代码: <el-form-item label"系统" prop"Key"> <el-select v-model"addForm.Key" multiple placeholder"请选择" change"$forceUpdate()"> <el-option v-for"item …

Typescript高级用法

TypeScript 是一种类型安全的 JavaScript 超集&#xff0c;除了基本类型和对象类型之外&#xff0c;TypeScript 还提供了一些高级类型系统&#xff0c;使得我们可以更好地处理复杂的数据结构和业务逻辑。本文将深入探讨 TypeScript 的高级类型系统&#xff0c;以更好地理解和使…

【AI大模型应用开发】【综合实战】AI+搜索,手把手带你实现属于你的AI搜索引擎(附完整代码)

现在市面上有很多的AI搜索的应用或插件&#xff0c;一直想学习其背后的实现原理。今天咱们就学习一下&#xff0c;并且亲自动手实践&#xff0c;从0开始&#xff0c;搭建一个自己的AI搜索引擎。最终实现效果如下&#xff1a; 话不多说&#xff0c;开干。 本文代码参考&#xff…

累加求和-C语言

1.问题&#xff1a; 计算123……100的和&#xff0c;要求分别用while、do while、for循环实现。 2.解答&#xff1a; 累加问题&#xff0c;先后将100个数相加。要重复进行100次加法运算&#xff0c;可以用循环结构来实现。重复执行循环体100次&#xff0c;每次加一个数。 3.代…

02DSP学习-了解syscfg

不是哥们儿&#xff0c;学习DSP为什么不是上来就写代码啊&#xff0c;说了一堆&#xff0c;写小说呢啊&#xff1f; 你别着急&#xff0c;学习DSP本身&#xff0c;真不需要写多少代码&#xff0c;我们需要的写的是自己的算法。开车知道方向盘、油门、刹车、后视镜之后也能开&a…

【SpringBoot详细教程】-03-整合Junit【持续更新】

JUnit是一个用于Java编程语言的测试框架。它支持自动化单元测试&#xff0c;可以帮助开发人员测试代码的正确性和健壮性。JUnit提供了一组注解、断言和测试运行器&#xff0c;可以方便地编写和运行单元测试。 SpringBoot 整合 junit 特别简单&#xff0c;分为以下三步完成 在…

cmake--file

教程 参数 需要指定文件后缀 GLOB 只搜索当前目录 GLOB_RECURSE &#xff1a;搜索当前目录和其子目录&#xff08;递归搜索&#xff09; RELATIVE 相对于哪个路径进行搜索&#xff0c;获取文件的相对路径。 使用RELATIVE和不使用RELATIVE的区别&#xff1a; 1&#xff…

《让手机秒变超级电脑!ToDesk云电脑、易腾云、青椒云移动端深度体验》

前言 科技发展到如今2024年&#xff0c;可以说每一年都在发生翻天覆地的变化。云电脑这个市场近年来迅速发展&#xff0c;无需购买和维护额外的硬件就可以体验到电脑端顶配的性能和体验&#xff0c;并且移动端也可以带来非凡体验。我们在外出办公随身没有携带电脑情况下&#x…

MudBlazor:一个UI简洁美观漂亮的Blazor开源组件!

Blazor&#xff0c;作为.NET生态系统中的一个革命性框架&#xff0c;使得可以使用C#来全栈开发Web应用。下面推荐一个Blazor开源UI组件MudBlazor&#xff0c;方便我们快速开发。 01 项目简介 MudBlazor 是一个开源的 .NET 库&#xff0c;它为 Blazor 应用程序提供了一套丰富的…

Java JUC(二) Synchronized 基本使用与核心原理

Java JUC&#xff08;二&#xff09; Synchronized 基本使用与核心原理 随着 Java 多线程开发的引入&#xff0c;程序的执行效率和速度都取得了极大的提升。但此时&#xff0c;多条线程如果同时对一个共享资源进行了非原子性操作则可能会诱发线程安全问题&#xff0c;而线程安全…

前端报错401 【已解决】

前端报错401 【已解决】 在前端开发中&#xff0c;HTTP状态码401&#xff08;Unauthorized&#xff09;是一个常见的错误&#xff0c;它表明用户试图访问受保护的资源&#xff0c;但未能提供有效的身份验证信息。这个错误不仅关乎用户体验&#xff0c;也直接关系到应用的安全性…

JAVA开源项目 学科竞赛管理系统 计算机毕业设计

本文项目编号 T 047 &#xff0c;文末自助获取源码 \color{red}{T047&#xff0c;文末自助获取源码} T047&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

修牛蹄视频哪里找?修牛蹄的解压视频素材网站分享

在现代农业和畜牧业中&#xff0c;牛蹄修剪技术的重要性不言而喻&#xff0c;不仅直接关联到牲畜的健康&#xff0c;也对农场的整体经济收益产生巨大影响。对于新手畜牧工作者而言&#xff0c;挑选出优秀的学习资源尤为关键。今天&#xff0c;我将为大家推荐几个提供优质牛蹄修…

烤羊肉串引来的思考——命令模式

文章目录 烤羊肉串引来的思考——命令模式吃烤羊肉串&#xff01;烧烤摊vs.烧烤店紧耦合设计命令模式松耦合设计进一步改进命令模式命令模式的作用 烤羊肉串引来的思考——命令模式 吃烤羊肉串&#xff01; 时间&#xff1a;6月23日17点  地点&#xff1a;小区门口  人物…

企业EMS -能源管理系统-能源在线监测平台

一、介绍 基于SpringCloud的能管管理系统-能源管理平台源码-能源在线监测平台-双碳平台源码-SpringCloud全家桶-能管管理系统源码 二、软件架构 二、功能介绍 三、数字大屏展示 四、数据采集原理 五、软件截图