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

news2024/9/27 5:46:24

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(){
   @Override
   public 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;//下一轮让出cpu
              lock.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(){
            @Override
            public 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;//下一轮让出cpu
                       lock.notify();//给另外的线程开锁
                    }
                }
            }
        };
         Thread thread2=new Thread(){
            @Override
            public  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.coloradmin.cn/o/1992711.html

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

相关文章

写给小白程序员的一封信

文章目录 1.编程小白如何成为大神?大学新生的最佳入门攻略2.程序员的练级攻略3.编程语言的选择4.熟悉Linux5.学会git6.知道在哪寻求帮助7.多结交朋友8.参加开源项目9.坚持下去 1.编程小白如何成为大神?大学新生的最佳入门攻略 编程已成为当代大学生的必…

音视频开发,最新学习心得与感悟

音视频技术的知识海洋浩瀚无垠,自学之路显得尤为崎岖,技术门槛的存在是毋庸置疑的事实。 对于渴望踏入这一行业的初学者而言,学习资源的匮乏成为了一道难以逾越的障碍。 本次文章主要是给大家分享音视频开发进阶学习路线,虽然我…

三大口诀不一样的代码,小小的制表符和换行符玩的溜呀

# 小案例,打印输出加法口诀 for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}{i}{ji}".strip(),end\t)print() print(\n) for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}x{i}{j*i}",end\t)print…

[Spring] Spring AOP

🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…

【Linux】sudo提升权限(入门)

相关专栏:《Linux》 目录 1. sudo功能介绍 2. 任何人都能用 sudo 吗? (1)查看配置文件/etc/sudoers (2)修改/etc/sudoers提权 3. 改变sudo输入密码时间 4. 显示sudo 密码 5.常见 sudo 命令 -k 参数 …

ajax part4

图片上传 <!DOCTYPE html> <lang"en"><head>cmeta charset"UTF-8><meta http-equiv"X-UA-Compatibleb content" IEedge"><meta name"viewportR content" wiclthdevic6-widths initial-scalel. 0"&…

做报表用什么工具?不想再用Excel了!!!

一、什么是中国式报表&#xff1f; 不知道大家现在还是使用Excel来制作报表&#xff0c;然后跟领导汇报工作吗&#xff1f;虽然Excel功能很强大&#xff0c;但是用Excel做过中国式报表的小伙伴一定知道它的制作过程有多复杂。 中国式报表可以用一句话简单概括&#xff1a;格式…

C++笔试强训11

文章目录 一、选择题1-5题6-10题 二、编程题题目一题目二 一、选择题 1-5题 A. 不是任何一个函数都可定义成内联函数&#xff1a;这是正确的。因为内联函数需要在编译时展开&#xff0c;如果函数体过大或包含复杂的控制结构&#xff08;如循环、递归等&#xff09;&#xff0c…

Linux/C 高级——分文件编程

1.头文件&#xff1a;.h结尾的文件 头文件引用、宏定义、重命名typedef、结构体、共用体、枚举的定义、函数声明、外部引用extern。 一般全局变量不会定义在头文件中 2.源文件&#xff1a;.c结尾的文件 包含main函数的.c文件&#xff1a;main函数 包含子函数的.c文件&#xff1…

【LLM】-17-会话存储

目录 1、会话存储类型 2、版本代码说明 3、对话缓存存储 3.1、示例代码 3.2、响应response说明 3.3、流式输出 3.4、添加提示词模板 3.5、指定回答语言 4、限制令牌数存储 4.1、trim_messages 4.1.1、自定义tokens计数器 4.1.2、自定义tokens计数器 4.2、完整chat…

HookNet- 用于病理全切片图像的多分辨率语义分割模型|顶刊精析·24-08-08

小罗碎碎念 今天分享的这篇文章是关于一种名为HookNet的新型语义分割模型&#xff0c;它专为病理学全切片图像设计&#xff0c;于2021年发表于《Med Image Anal》&#xff0c;目前IF10.7。 作者角色姓名单位&#xff08;中文翻译&#xff09;第一作者Mart van Rijthoven荷兰Ra…

Spring-boot 集成 SocketIO(看这一篇就够了)

1 前言 1.1 什么是 SocketIO ? Socket.IO 是一个可以在浏览器与服务器之间实现实时、双向、基于事件的通信的工具库。 Socket.IO 能够在任何平台、浏览器或设备上运行,可靠性和速度同样出色。 1.2 websocket和socket.io区别&#xff1f; websocket a&#xff1a;一种让客户…

video标签,去除上下默认边距

不知道为什么&#xff0c;video标签上下会有空白 清除方法 style"width 100%; height100%; object-fit: fill"

校园二手物品交易网站/校园闲置物品交易系统

摘 要 本文论述了校园二手物品交易网站的设计和实现&#xff0c;该网站从实际运用的角度出发&#xff0c;运用了计算机网站设计、数据库等相关知识&#xff0c;网络和JSP技术、SSM框架Mysql数据库设计来实现的&#xff0c;网站主要包括学生注册、学生登录、浏览商品、搜索商品…

Vue+Element Plus后台管理主界面搭建实现

​ 续接Django REST Framework&#xff0c;使用Vite构建Vue3的前端项目 1. 后台管理系统主界面框架搭建 后台系统主界面搭建 新建后台管理文件目录 完成后台整体布局 // 1.主界面 index.vue<script setup lang"ts"></script><template><el-…

应用层HTTP协议

文章目录 应用层HTTP协议1、HTTP协议概念2、URL&#xff08;统一资源定位符&#xff09;2.1、URL的encode&#xff08;编码&#xff09;和decode&#xff08;解码&#xff09; 3、HTTP请求和响应报头格式3.1、请求报头3.2、响应报头 4、HTTP的方法4.1、GET方法4.2、POST方法4.3…

生成模型VAE

VAE likelihood-basedELBOVAESGVB估计器和AEVB算法重参数化 likelihood-based likelihood-based generative models是生成模型的一类范式&#xff0c;通过最大化所有观测数据的似然函数来学习模型参数。 该怎么去理解likelihood-based&#xff0c;基于似然的生成模型&#xf…

互联网重构“规则制定权”,周期性谋咒开始轮转!

周期“魔咒”又开始轮转了。 产业趋势叠加资本周期&#xff0c;使得任何产业都有其周期性规律&#xff0c;传统资源产业是如此&#xff0c;科技产业亦非例外。 刚刚迎来30周年庆的中国互联网赛道就正处于新一轮小周期的节点。随着移动用户量逐渐被开发利用至阶段性顶峰&#…

学习c语言第24天(练习)

编程题 第一题 最大公约数最小公倍数求和 //求最大公约数和最小公倍数之和 //暴力求解 //int main() //{ // int n 0; // int m 0; // while (scanf("%d %d", &n, &m)2) // { // int min n < m ? n : m; // int max n > m ? n : m; //…

原神4.8版本角色数据

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>原神4.8版本角色数据</title><style>…