Java学习Day24:基础篇14:多线程

1.程序、进程和线程

程序

进程

  • 进程(process)是程序的一次执行过程,或是一个正在执行的程序。是一个动态的过程:有它自身的产

生、存在和消亡的过程。

  • 如:
    • 运行中的QQ
    • 运行中的音乐播放器
    • 视频播放器等;
  • 程序是静态的,进程是动态的;
  • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域;
  • 进程是一个具有一定独立功能的应用程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分

配和调度的一个独立单位,是应用程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。

  • 进程一般由程序,数据集合和进程控制块三部分组成。
    • 程序用于描述进程要完成的功能,是控制进程执行的指令集;
    • 数据集合是程序在执行时所需要的数据和工作区;
    • 程序控制块包含进程的描述信息和控制信息是进程存在的唯一标志。
  • 进程具有的特征:
    • 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
    • 并发性:任何进程都可以同其他进程一起并发执行;
    • 独立性:进程是系统进行资源分配和调度的一个独立单位,不同进程的工作互相不影响;
    • 结构性:进程由程序,数据和进程控制块三部分组成;
    • 制约性:因访问共享数据/资源或进程间同步而产生制约;

进程与程序联系与区别:

  1. 两者联系
    • 进程是操作系统处于执行状态程序的抽象
    • 程序 = 文件(静态的可执行文件)
    • 进程 = 执行中的程序 = 程序 + 执行状态
  • 同一个程序的多次执行过程对应为不同进程
      • 例如:多次使用命令ls的执行对应多个进程。
  • 进程执行需要的资源
    • 内存:保存代码和数据
    • cpu:执行指令
  1. 两者区别
    • 进程是动态的,程序是静态的。
    • 程序是有序代码的集合
    • 进程是程序的执行
    • 进程是暂时的,程序是永久的。
      • 进程时一个状态变化的过程
      • 程序可长久保存
    • 进程与程序的组成不同。
      • 进程的组成包括程序,数据,和进程控制块。
  • 简单来说:我们写好的一个代码,要想让它运行起来,首先需要将这个程序加载到内存中,程序一旦运行

那么就是一个进程,就需要给进程分配资源建立PCB等动作。说白了,程序只是一个存储在我们硬盘上的文件

而已断电不会消失,但是进程是在动态运行的一个实体,进程执行结束相关信息就会被释放了。

线程

  • 在早期的操作系统中并没有线程的概念,进程是拥有资源和独立运行的最小单位,也是程序执行的最小单

位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。

  • 简而言之:线程是进程的一个执行单元。比进程更小的独立运行的基本单位。
  • 注:一个程序至少一个进程,一个进程至少一个线程。比如视频中同时有声音、图像、弹幕等;
  • 并行和并发:https://www.cnblogs.com/fulaien/p/16489366.htmlicon-default.png?t=N7T8https://www.cnblogs.com/fulaien/p/16489366.html
    • 并行
      • 真正的多线程
    • 并发
      • 假象的多线程

2.线程的创建和启动

方式一:继承Thread类

Thread thread1=new Thread(){@Overridepublic void run() {suo();}public void suo()  {while (true){//定义单个线程循环synchronized (lock){//上安全锁,以下判断只能互斥进行if (!thread1turn){try {lock.wait();//轮不到就等} catch (InterruptedException e) {throw new RuntimeException(e);}}if (count==0)break;//到点了就跳System.out.println("我是线程1     "+count);count--;thread1turn=false;//下一轮让出cpulock.notify();//给另外的线程开锁}}}

1、如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。

2、run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。

3、想要启动多线程,必须调用start方9法。

4、一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异 常“IllegalThreadStateException”。

方式二:实现Runnable接口的方式

  • 实现Runnable接口
    • Thread(Runnable target, String name):创建新的Thread对象;
  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法,把新线程要做的事写在run方法中
  3. 创建自定义的Runnable的子类对象
  4. 通过Thread类含参构造器创建线程对象,将Runnable接口的子类对象作为实际参数传递给Thread 类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

两种创建方式的对比

 

1. 继承与实现

  • Thread类:是一个类,需要通过extends关键字来继承。这意味着,如果一个类继承了Thread类,那么它就不能再继承其他类了,因为Java不支持多重继承。
  • Runnable接口:是一个接口,需要通过implements关键字来实现。实现Runnable接口的类可以自由地继承其他类,从而避免了Java单继承的限制。

2. 线程创建方式

  • 继承Thread类:通过继承Thread类并重写其run方法,然后创建该类的实例并调用其start()方法来创建线程。这种方式简单直接,但限制了类的继承能力。
  • 实现Runnable接口:通过实现Runnable接口并重写其run方法,然后创建Thread类的实例,并将Runnable实例作为构造参数传递给Thread类,最后调用Thread实例的start()方法来创建线程。这种方式更加灵活,允许将任务(Runnable实现类的实例)和线程(Thread实例)分离开来,从而更容易地实现线程之间的资源共享和任务分配。

3. 资源共享

  • Thread类:由于Thread类本身是类的形式,其内部可以直接定义实例变量等成员,这些成员默认是线程隔离的,即每个Thread对象都有自己的实例变量副本。因此,在多个线程之间共享数据时,需要额外的同步机制。
  • Runnable接口:实现Runnable接口的类中的实例变量等成员是共享的,因为它们通常被封装在另一个类中(比如一个服务类)。这使得实现Runnable接口的方式更适合于需要多个线程共同访问和修改同一份数据资源的场景。

4. 灵活性与扩展性

  • Thread类:虽然简单直接,但在某些复杂的场景下可能会显得不够灵活。例如,当你需要让你的类继承自其他类(而不是Thread类)时,你就无法使用继承Thread类的方式来创建线程了。
  • Runnable接口:由于其基于接口的实现方式,使得它更加灵活和可扩展。你可以自由地让你的类继承自其他类,并通过实现Runnable接口来使其具备多线程能力。此外,你还可以将任务(Runnable实现类的实例)作为参数传递给线程池等高级并发工具来使用,从而进一步提高程序的并发性能。

综上所述,Thread类和Runnable接口在Java多线程编程中各有优劣,具体使用哪种方式取决于你的具体需求和代码结构。在实际开发中,建议优先考虑使用实现Runnable接口的方式来创建线程,因为它更加灵活和可扩展。

3.线程的状态(生命周期)

  • JDK中用Thread.State类定义了线程的几种状态。
  • 要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线

程,在它的一个完整的生命周期中通常要经历如下的五种状态:

  1. 新建状态(New):当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。
    • 如:Thread t = new MyThread();
  1. 就绪状态(Runnable):处于新建状态的线程对象被start()后(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,已经加入了操作系统的任务调度队列,等待被操作系统调度执行,并不是说执行了t.start()此线程立即就会执行;看CPU的执行权
    1. start
  2. 运行状态(Running):当就绪状态的线程被操作系统的任务调度机制调度到,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;run()方法定义了线程的操作和功能; 抢到了CPU的执行权,开始运行
  3. 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其再次进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
    1. 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;a
    2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
    3. 其他阻塞 : 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  4. 死亡状态(Dead):线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。
  5. 就绪状态转换为运行状态:当此线程得到处理器资源;运行状态转换为就绪状态:当此线程主动调用 yield()方法或在运行过程中失去处理器资源。运行状态转换为死亡状态:当此线程线程执行体执行完毕或发生了异常。
  • 注意
    • 此处需要特别注意的是:当调用线程的 yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。

4.线程控制(逻辑控制)

1.线程控制:获取当前线程、名称和设置名称

Mythread m= new Mythread("haha"); System.out.println(m.getName());

2.休眠线程:Thread.sleep(2000);

3.守护线程    t2.setDaemon(true);

4.加入线程    join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续;

5.礼让线程    Thread.yield();

6.设置线程优先级   thread.setPriority(10);

7.线程等待wait   

8.线程同步方法    synchronized关键字加到方法声明上

通过声明synchronized保证线程互斥安全执行的条件下利用wait阻塞和notifyAll()来实现多线程互斥执行;

=========================================================================

public class thraed1 {static boolean thread1turn=true;//定义顺序标签static  int count =20;//定义循环次数static Object lock = new Object();public static void main(String[] args) {Thread thread1=new Thread(){@Overridepublic void run() {suo();}public void suo()  {while (true){//定义单个线程循环synchronized (lock){//上安全锁,以下判断只能互斥进行if (!thread1turn){try {lock.wait();//轮不到就等} catch (InterruptedException e) {throw new RuntimeException(e);}}if (count==0)break;//到点了就跳System.out.println("我是线程1     "+count);count--;thread1turn=false;//下一轮让出cpulock.notify();//给另外的线程开锁}}}};Thread thread2=new Thread(){@Overridepublic  void run() {suo();}public void suo()  {while (true){synchronized (lock){if (thread1turn){try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}if (count==0)break;System.out.println("我是线程2     "+count);count--;thread1turn=true;lock.notify();}}}};thread1.start();thread2.start();}
}

=========================================================================

5.多线程共享数据、线程安全

6.线程间的通信

7.线程池

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【大模型从入门到精通13】openAI API 构建和评估大型语言模型(LLM)应用1

这里写目录标题 构建和评估大型语言模型(LLM)应用开发性能评估指标从开发到部署高风险应用LLM应用开发的最佳实践和建议从小处着手快速迭代自动化测试根据应用需求定制评估考虑伦理影响 构建和评估大型语言模型(LLM)应用 开发和部…

Sqli-labs-master靶场--布尔盲注

目录 1、布尔盲注 2、布尔盲注的流程(以靶场less-8为例) 2.1输入id尝试是否存在注入点 2.1.1通过以上尝试,联想到可能是布尔盲注 2.2猜测数据库长度 2.3获取数据库名 2.3.1python脚本获取 代码: 获取结果为: …

Hive SQL ——窗口函数源码阅读

前言 使用Starrocks引擎中的窗口函数 row_number() over( )对10亿的数据集进行去重操作,BE内存溢出问题频发(忘记当时指定的BE内存上限是多少了.....),此时才意识到,开窗操作,如果使用 不当,反而…

1. js混淆-源码乱码

目录 调试干扰参数逆向 调试干扰 打开开发者工具,首先会进入 setInterval 生成的 debugger 将 uzt.js uyt.js 内容替换 将这两个文件的内容置空,并刷新页面就可以正常调试了 参数逆向 点击翻页,可以发现 https://match.yuanrenxue.cn/api…

Arduino导入实例程序的过程,实例包文件却编译显示缺失文件

参考中实例程序中的readme.txt 导入方式 下面是文档中的使用方式 1.基本信息: 本例程是基于Arduino进行开发的,例程均在E-Paper ESP8266 Driver Board上进行了验证;2.基本使用:方法1:将整个esp8266-waveshare-epd文件夹复制到C…

【Go】通过反射解析对象tag信息,实现简易ORM

反射是运行时,需要在运行时解析类型信息,编译期无法优化这些操作,因此比编译时已知类型信息的直接调用效率要低。 package mainimport ("fmt""reflect""strings" )type Person struct {Name string json:&quo…

PicGo + gitee 免费搭建个人图床

目录 1 图床概念2 使用gitee和PicGo搭建图床流程2.1 下载安装PicGo工具 3 图片上传错误处理3.1 PicGo客户端提示404错误信息图片上传失败3.2 PicGo客户端提示400错误信息图片上传失败 1 图床概念 ​ "图床"是一个网络术语,它指的是一种用于存储和托管图片…

理解张量拼接(torch.cat)

拼接 维度顺序:对于 3D 张量,通常可以理解为 (深度, 行, 列) 或 (批次, 行, 列)。 选择一个dim进行拼接的时候其他两个维度大小要相等 对于三维张量,理解 torch.cat 的 dim 参数确实变得更加抽象,但原理是相同的。让我们通过一…

算法力扣刷题记录 六十九【动态规划基础及509. 斐波那契数】

前言 调整一下做题顺序,多个章节同步进行,穿插练习。可以在各章节的专栏中找同一类。 记录 六十九【动态规划基础】。 一、动态规划理论基础学习 参考学习链接 二、509. 斐波那契数 2.1 题目阅读 斐波那契数 (通常用 F(n) 表示&#x…

html+css+js网页设计 中国移动5个页面(带js)

htmlcssjs网页设计 中国移动5个页面(带js) 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xf…

Cpp中的this指针--复习记录

1.什么是this指针? 每个类都有一个this指针,我们的非静态成员函数可以通过这个this指针来操作对象的成员属性。this指针存储的就是类的实例的地址,this指针时时刻刻指向的都是这个实例对象本身。 由下图可知: 我在主函数中栈上创建了一个类的实例(由操…

【Python-实操】LabelMe to YOLOv8 Converter

LabelMe to YOLOv8 Converter 这是一个 Python 脚本,用于将 LabelMe 标注工具导出的 JSON 文件转换为 YOLOv8 格式的标注文件,并同时在图像上绘制标注的多边形。 功能 读取 LabelMe JSON 文件。解码并显示图像。从 classes.txt 文件加载类别标签。将多…

Java | Leetcode Java题解之第327题区间和的个数

题目&#xff1a; 题解&#xff1a; class Solution {public int countRangeSum(int[] nums, int lower, int upper) {long sum 0;long[] preSum new long[nums.length 1];for (int i 0; i < nums.length; i) {sum nums[i];preSum[i 1] sum;}BalancedTree treap ne…

Java参数传递

Java参数传递 一、 方法重载 一个类中可以存在多个同名的方法&#xff0c;只要这些方法的参数列表不同即可。 参数列表不同&#xff1a;参数个数或者参数类型不同方法重载与修饰符、返回值类型等统统无关&#xff0c;只看参数列表 二、 可变个数的形参 从Java5.0开始&…

陶瓷材质的防静电架空地板越来越受欢迎的原因

目前市面上的陶瓷防静电架空地板主要分为两种&#xff1a;钢基和硫酸钙基。前者是以全钢冲孔裸板作为板基&#xff0c;经粘接、固定整型和灌浆的方式加工而成&#xff0c;后者是以复合硫酸钙板为基材&#xff0c;表面粘接防静电陶瓷砖&#xff0c;四周导电PVC边条封边。近年来陶…

【C++】vector 的模拟实现

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

02_快速启动 Demo 创建 Electron 项目、electron-forge 搭建一个 electron 项目、手动创建electron项目

快速启动 Demo 创建 Electron 项目 一、克隆一个仓库、快速启动一个项目二、electron-forge 搭建一个 electron 项目三、手动搭建一个 electron 项目四、开发工具中配置 Eslint 一、克隆一个仓库、快速启动一个项目 要使用 git 的话首先电脑上面需要安装 git //克隆示例项目的…

Qt3D给圆环等立体图形添加纹理图片

添加纹理图片&#xff0c;首先需要自己找一个纹理图&#xff0c;当然了&#xff0c;随便什么图片都行。 创建3D图形的主要步骤查看另一篇文章。 这里主要代码如下&#xff1a; 使用QTextureLoader加载图片&#xff0c;图片路径需为qrc:/的路径。 auto *planeTransform1 ne…

嵌入式学习day13(C高级Linux命令)

一丶进程管理命令 1.grep 功能&#xff1a;从文件中查找字符串 格式&#xff1a;grep "要查找的字符串" 文件名 精确查找&#xff1a;grep "\<要查找的字符串\>" 文件名 结合ps以及管道&#xff1a;ps -ef | grep a.out: 从进程信息中查找带…

10个理由告诉你,为什么鸿蒙是下一个职业风口!

在当今科技飞速发展的时代&#xff0c;新的技术和趋势不断涌现&#xff0c;为人们带来了前所未有的机遇和挑战。鸿蒙操作系统作为我国自主研发的创新成果&#xff0c;正逐渐成为科技领域的焦点&#xff0c;被认为是下一个职业风口。 10个理由告诉你&#xff0c;为什么鸿蒙是下一…