上厕所也要看的 一篇博客学会线程 进程 并行 并发 Thread Runnable等

news2025/1/18 9:02:00

线程相关概念

  • 程序

  • 为完成特点的任务,用某种语言编写的一组指令的集合,简单来说就是我们的代码

  • 进程

  1. 定义:进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
  2. 内存分配:进程有自己独立的地址空间。在一个进程内,任何内存的改变(如变量的更改)不会影响到其他进程。
  3. 资源开销:创建、销毁进程的开销较大,因为操作系统需要为进程分配和回收资源。
  4. 通信:进程间通信(IPC)比较复杂,常用的IPC机制包括管道、消息队列、共享内存、信号等。

  5. 执行方式:进程可以包含多个线程,进程的每个实例独立执行。

        程序是运行的代码,进程是程序运行之后可以动的活起来的东西

  • 线程

  1. 定义:线程是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源。
  2. 内存分配:同一进程中的线程共享地址空间、全局变量和文件描述符等资源。这意味着一个线程对某些变量的更改会影响到同一进程中的其他线程。

  3. 资源开销:创建、销毁线程的开销较小,因为线程之间共享进程的资源,不需要像进程那样分配独立的资源。

  4. 通信:线程之间的通信更简单,因为它们共享相同的内存空间,可以通过共享变量直接进行通信。

  5. 执行方式:线程是并发执行的,可以同时执行多个线程,以提高程序的并发性和性能。

     线程是由进程创建的,是进程的一个实体,一个进程可以拥有多个线程,如下图

     下载的程序运行作为进程,进程里的三个下载对应三个线程。

     每个进程拥有独立的内存空间,内存空间内变量不相互影响。

     一个进程可以拥有多个线程,线程共享地址空间,线程之间相互影响。

  • 并发:

  • 同一个时刻,多个任务交替进行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发

  • 并行:

  • 同一时刻,多个任务同时进行,多核cpu可以实现并行。

线程的基本使用

  • 创建线程的两种方式
  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法。

继承Thread类重写run方法 

//1.当一个类继承了Thread类,该类就可以当做线程使用
//2.我们会重写Thread方法,写上自己的业务代码
//3.run Thread类 实现了 Runnable接口的run方法
class Cat extends Thread{
    int times=0;
   @Override
    public void run() {
       while (true) {
           //该线程每隔一秒,在控制台输出“喵喵,我是小猫咪”
           System.out.println("喵喵,我是小猫咪"+(++times));
           //让该线程休眠一秒 ctrl+alt+t
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
           if(times==80){
               break;
           }
       }
   }
}

我们设计一个猫猫类继承Thread,并且重写run方法写上自己的业务逻辑,实现功能,通过While(true)一直调用,直到times==80退出调用

接下来我们在main方法中调用并且创建该线程启动进程(start启动线程)

public class Thread01 {
    public static void main(String[] args) {
          //创建Cat对象,可以当做线程使用
        Cat cat=new Cat();
        cat.start();
    }
}

我们来看结果

我们进一步讨论一下线程的运行

多线程机制

启动进程之后在main里面先开启线程,通过start方法又开启了一个线程

只有最后一个线程结束,你的进程才会结束,而非主方法结束,进程才会结束

比如主线程里面打印60次,Thread线程里面打印80次,还是等80次打印完才退出进程。

为什么是Start方法

我们看我们在主线程里面调用线程的代码

public static void main(String[] args) {
          //创建Cat对象,可以当做线程使用
        Cat cat=new Cat();
        cat.start();
    }

我们明明在写线程方法的时候用的是run,为什么在调用的时候用的是start呢

  • run方法就是一个普通的方法,没有真正的启动一个线程,就会把run方案执行完毕,才向下执行
  • 当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
  • 主线程与子线程交替执行

如图所示

在start()方法中,真正实现多线程效果的是start0方法,这是一个native方法(本地方法),由JVM调用。

实现Runnable接口,重写run方法。

在Runnable接口中没有Thread类的start方法,所以我们不能直接调用。

而是

创建了Thread对象,把dog对象(实现Runnable),放入Thread

代码示例

/通过实现Runnable类来开发线程
public class Thread02 {
    public static void main(String[] args) {
       Dog dog=new Dog();
       //dog.start();这里不能调用start
       //创建了Thread对象,把dog对象(实现Runnable),放入Thread
        Thread t1=new Thread(dog);
        t1.start();
    }
}

class Dog implements Runnable {//通过实现runnable接口,开发线程
    int count=0;
    @Override
    public void run() {
        while (true){
            System.out.println("小狗汪汪叫。。hi"+(++count)+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(count==10){
                break;
            }
        }
    }
}

运算结果

当然,关于Runnable的使用涉及到代理模式,我们接着展开说明

Runnable代理模式

先模拟一个线程代理类,里面有一个属性Runnable。

在构造器里面接收一个实现了Runnable接口的对象target

Thread里面的start方法里面调用的start0方法是真正实现多线程的方法。

class ThreadProxy implements Runnable{
private Runnable target=null;
    @Override
    public void run() {
        if(target==null){
            target.run();
        }
    }

    public ThreadProxy(Runnable target) {
        this.target = target;
    }

    public void start(){
        start0();
    }

    public void start0(){
        run();
    }
}

多个子线程案例

我们通过Runnable接口进行实现,我们直接来看代马

public class Thread03 {
    public static void main(String[] args) {
       T1 t1 = new T1();
       T2 t2 = new T2();
       Thread t3 = new Thread(t1);
       Thread t4 = new Thread(t2);
       t3.start();
       t4.start();
    }
}

class T1 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        while (true) {
            //每隔一秒输出“hello,world”,输出10次
            System.out.println("hello,world" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 10) {
                break;
            }
        }
    }
}

class T2 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        while (true) {
            //每隔一秒输出“hi”,输出5次
            System.out.println("hi " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 5) {
                break;
            }
        }
    }
}

在main线程里面又创建了两个线程,不难理解。 

如果main线程先退出,不影响子线程1,2的进行 

继承Threadvs实现Runnable的区别

我们来通过具体案例去理解这个

public static void main(String[] args) {
       T1 t1 = new T1();
       T2 t2 = new T2();
       Thread t3 = new Thread(t1);
       Thread t4 = new Thread(t1);

在通过实现Runnable 所创建的类的实例化可以将它的实例化对象用于多个线程,适合多个线程共享一个资源的情况

我们再来看一个题目

使用多线程,同时模拟三个窗口同时售票100张

我们直接看代码

通过继承Thread类实现

public class SellTicket {
    public static void main(String[] args) {
     //测试
        SellTicket01 sellTicket01=new SellTicket01();
        SellTicket01 sellTicket02=new SellTicket01();
        SellTicket01 sellTicket03=new SellTicket01();

        sellTicket01.start();//启动收票线程
        sellTicket02.start();
        sellTicket03.start();

        }
}

class SellTicket01 extends Thread{
    private static int ticketNUm = 100;
    @Override
    public void run() {
        while(true){
            if(ticketNUm <=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出了一张票"+"剩余票数"+(--ticketNUm));
        }
    }
}

接着我们运行一下

实现Runnable方法

//实现接口方法
class SellTicket02 implements Runnable{
    private static int ticketNUm = 100;
    @Override
    public void run() {
        while(true){
            if(ticketNUm <=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出了一张票"+"剩余票数"+(--ticketNUm));
        }
    }
}

调用

 System.out.println("===使用实现接口方式来售票===");
        SellTicket02 sellTicket02=new SellTicket02();

        new Thread(sellTicket02).start();//第一个线程——窗口
        new Thread(sellTicket02).start();//第二个线程——窗口
        new Thread(sellTicket02).start();//第三个线程——窗口

 为什么会出现票数为负数的情况呢

我们假设票数还剩两张,此时t1进入线程,但是票数还没有完成减少的时候,t2,t3也进入了线程,所以这个时候票数减少的操作还会执行,出现负数

通知线程终止

在我们学习了之前线程的运行的时候我们使用While(true)使线程不进行终止,那我们如果需要终止线程的思路就是将While(true)的true进行变换,我们可以再main线程里面进行控制

我们先来写继承Threa类实现线程

我们来看代码

class T extends Thread{
    int count=0;
    //设置一个控制变量
    private boolean loop=true;
    @Override
    public void run(){
        while (loop){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("T 运行中......"+(++count));
        }
    }
    public void setLoop(boolean loop){
        this.loop = loop;
    }
}

在上述代码中,我们将While中的布尔值设置成可以进行赋值的loop,并且写了set方法去实现loop的赋值,我们回到主线程去看如何进行控制

public class ThreadExit
{
    public static void main(String[] args) {
      T t = new T();
      t.start();

      //如果希望main线程去控制t1线程的终止,必须可以修改Loop
        //让t1退出run方法,从而终止t1线程->通知方式

        //让主线程休眠10秒,再通知退出
        System.out.println("main线程休眠10秒");
        try {
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        t.setLoop(false);
    }
}

 我们通过让计算机10秒钟之后休眠,写了Thread的sleep方法,并且将setloop写在里面,实现了线程的终止。

我们来看运算结果。

在运行了10秒之后,退出子线程。

线程的常用方法

我们来应用上述的这些方法

public class ThreadMethod01 {
 public static void main(String[] args) throws InterruptedException {
        //测试相关方法
        Threaddelete t = new Threaddelete();
        t.setName("徐子昂");
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();//启动子线程


        //主线程打印5 hi,然后中断子线程的休眠
     for(int i = 0; i < 5; i++){
        Thread.sleep(1000);
         System.out.println("hi"+i);
     }
     t.interrupt();//当执行到这里,就会中断t线程的休眠
 }
}
class Threaddelete extends Thread {//自定义线程类
    @Override
    public void run() {
        while (true) {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "吃包子~~~~~~~" + i);
            }
            try {
                System.out.println(Thread.currentThread().getName() + "休眠中~~~~~");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                System.out.println(Thread.currentThread().getName() + "被 interrupt了");
            }
        }
    }
}

在该线程中,我们使用了interrupt方法使线程在休眠的过程中被中断休眠,因为当main线程的i=5的时候会中断休眠的线程(被catch方法捕捉到

我们来看第二组常用方法

我们通过主线程和子线程吃包子的例子来给大家演示说明。

我们先创建一个子线程(牢大),功能是循环吃20个包子,吃一次休眠一会。我们再写 一个主线程,里面调用子线程,并且写自己吃包子的线程,分别模拟两个人

join方法

join方法可以理解为小弟给牢大让包子吃,小弟先和牢大一起吃,在吃到5个包子的时候小弟给牢大让包子,此时牢大调用join方法,当牢大线程执行完毕之后小弟继续吃包子,我们来看代码。

public class ThreadMethod2 {
    public static void main(String[] args) throws InterruptedException {
      T7 t7 = new T7();
      t7.start();

      for (int i = 1; i <=20; i++) {
          Thread.sleep(1000);
          System.out.println("主线程(小弟)吃了"+i+"包子");
          if(i==5) {
              System.out.println("主线程(小弟)让牢大先吃");
              t7.join();//这里相当于让t7线程执行完毕
              System.out.println("牢大吃完了,小弟接着吃");
          }
      }
    }
}

class T7 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <=20; i++) {
            try {
                Thread.sleep(1000);//休眠1秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("子线程(牢大)吃了"+i+"包子");
        }
    }
}

我们来看结果

yield方法

yield方法也有插队的作用,但是系统会更具实际情况进行分析要不要插队,如果可以满足两个进程的运行那就不需要进程插队,比如有一万个包子,那小弟和牢大一起吃就行,不需要让包子。

我们来看调用

public static void main(String[] args) throws InterruptedException {
      T7 t7 = new T7();
      t7.start();

      for (int i = 1; i <=20; i++) {
          Thread.sleep(1000);
          System.out.println("主线程(小弟)吃了"+i+"包子");
          if(i==5) {
              System.out.println("主线程(小弟)让牢大先吃");
              //t7.join();//这里相当于让t7线程执行完毕
              Thread.yield();//不一定成功
              System.out.println("牢大吃完了,小弟接着吃");
          }
      }
    }
}

运行结果

此时牢大和小弟交替吃包子,礼让失败。 

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

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

相关文章

Idea中,resources下面建文件夹踩坑

Idea中&#xff0c;resources下面建文件夹踩坑 直接上问题 mybatis无法加载xml&#xff0c;别划走&#xff0c;这个无法加载可不是配置写错了&#xff0c;继续看&#xff01; 首先看上图中的xml&#xff0c;可以确定的是项目中所有的配置都是正确的&#xff0c;但是运行mappe…

Apache Kafka各Api模块说明

Kafka API 微信公众号&#xff1a;阿俊的学习记录空间小红书&#xff1a;ArnoZhangwordpress&#xff1a;arnozhang1994博客园&#xff1a;arnozhangCSDN&#xff1a;ArnoZhang1994 Kafka 包含五个核心 API&#xff1a; Producer API 允许应用程序将数据流发送到 Kafka 集群中…

在java 中如何实现执行linux命令,通过post接口代理出来?

接口方式输入命令得返回结果 public AjaxResult doPost(HttpServletRequest request, HttpServletResponse response, String command) throws ServletException, IOException {// 设置响应内容类型 text/plain // response.setContentType("application/json"…

Linux——Harbor: 容器镜像的存储

K8s 如何通过harbor 拉取镜像&#xff1f; K8S 在那些情况下需要进行镜像的拉取&#xff1f; 在需要进行新的pod的调度时&#xff0c;基于镜像拉取策略&#xff0c;完成镜像的获取&#xff1a; Always: 在任何情况下都需要重新拉取镜像 即使设置为总是进行镜像拉取&#xff…

ERP系统是什么?ERP系统如何与数据库对接?

ERP系统的定义 1.企业ERP系统标准的定义来自于其英文原意&#xff0c;即企业资源规划(Enterprise Resource Planning)。企业资源计划系统是一种集成的软件系统&#xff0c;旨在帮助企业管理其资源。它可以协调各种不同的业务流程&#xff0c;例如供应链管理、采购、库存管理、…

arm架构ceph pacific部署

背景 合作伙伴实验室的华为私有云原来使用单点的nfs做为存储设备&#xff0c;现有两方面考量&#xff0c;业务需要使用oss了&#xff0c;k8s集群及其他机器也需要一套可扩展的分布式文件系统 部署ceph 初始机器配置规划 IP配置主机名Role10.17.3.144c8g1T数据盘ceph-node01…

2-122 文章复现:基于matlab的多智能体系统一致性算法的电力系统分布式经济调度策略

文章复现&#xff1a;基于matlab的多智能体系统一致性算法的电力系统分布式经济调度策略&#xff0c;应用多智能体系统中的一致性算法&#xff0c;以发电机组的增量成本和柔性负荷的增量效益作为一致性变量&#xff0c;设计一种用于电力系统经济调度的算法&#xff0c;通过分布…

透过3大海外案例,看百亿中老年眼镜市场创新发展方向

国内老花镜市场容量为342.15亿元 前言 全球人口老龄化的加速推进&#xff0c;中老年群体正逐渐成为消费市场中不可忽视的重要力量&#xff0c;每一个细微的市场变化都映射着社会结构与消费需求的深刻转型。 其中&#xff0c;中老年眼镜市场的新场景、新需求不断涌现&#xf…

西门子S7-200 SMART选型指南之产品订货信息

提供了S7-200 SMART控制器的产品订货号列表&#xff0c;用户可以根据订货号准确选择所需的产品型号。 S7-200SMART CPU 订货号 产品选型或方案确认需求需要结合工艺、应用场合等因素综合考虑&#xff0c;最终方案或订货号请与西门子销售或经销商确认。本文列出的订货号仅供参考…

Vite + Vue3 使用 cdn 引入依赖,并且把外部 css、js 文件内联引入

安装插件 pnpm i element-plus echarts axios lodash -S在 vite.config.js 引用 注意事项&#xff1a;element-plus 不能在 vite.config.js 中使用按需加载&#xff0c;需要在 main.js 中全局引入&#xff1b; import { resolve } from path import { defineConfig } from v…

晶振电路的设计一定要和单片机靠近,路线尽量短

晶振作为同步单片机全部工作的核心&#xff0c;首先要求高度可靠和稳定&#xff0c;所以离芯片越近&#xff0c;引线越短&#xff0c;受外来干扰就越小&#xff1b; 其次&#xff0c;一般晶振部分是整个电路板中频率最高的部分&#xff0c;也就是高频辐射最严重的部分&#xf…

Redis集群相关

目录 一、Redis主从集群 主从数据同步原理 全量同步 1&#xff09;为什么是基本一致而不是完全一致呢&#xff1f; 2&#xff09;上述过程还有一个问题&#xff0c;怎么判断是不是第一次同步&#xff1f; 增量同步 1&#xff09;master节点怎么知道slave节点与自己的数据…

关于Git Bash中如何定义alias

一、在一次临时Bash会话中使用alias 在Bash中直接输入alias xxdddd&#xff0c;xx为对应要执行的命令的缩写&#xff0c;dddd为要执行的命令&#xff0c;如alias ddcd /d&#xff0c;输入完成后&#xff0c;在Bash中输入dd&#xff0c;即可切换至D盘。 此种设置方式&#xff…

JavaSE——集合2:List(Iterator迭代器、增强for、普通for循环遍历集合)

目录 一、List (一)List接口基本介绍 二、List接口的常用方法 三、List集合的三种遍历方式 四、小练习——使用冒泡排序遍历集合 一、List (一)List接口基本介绍 List接口是Collection接口的子接口 public interface List<E> extends Collection<E> List集…

华大基因与NRL完成技转合作,为中东家庭提供更优质的产前筛查方案

今年年初&#xff0c;华大基因与中东地区最大的美国病理学家学会&#xff08;CAP&#xff09;认证实验室集团National Reference Laboratory&#xff08;NRL&#xff09;&#xff0c;顺利完成了无创产前基因检测技术&#xff08;NIPT&#xff09;技转合作&#xff0c;并向NRL实…

AI先行者工具撰写的烧脑短篇,你不可错过

以下文字是我用ai先行者所创造的文字。我就输入烧脑故事四个关键字 昨晚&#xff0c;我像往常一样刷着朋友圈&#xff0c;准备结束一天的工作前放松一下。突然间&#xff0c;我的好友小林发了一条状态&#xff1a;“如果有人发现我不见了&#xff0c;请不要报警。”这句话配上…

【JAVA毕业设计】基于Vue和SpringBoot的加油站管理系统

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

金蝶云星空与金蝶云星空对接集成采购订单查询连通采购订单新增(采购订单)

金蝶云星空与金蝶云星空对接集成采购订单查询连通采购订单新增(采购订单) 对接系统金蝶云星空 金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上&#xff0c;提供了标准的管理模式&#xff1b;通过标准的业务架构&#xff1a;多会计准则、多币别、多地点、多组织、多税制应用…

Prometheus+Grafana 监控 Kubernetes

文章目录 一、Prometheus介绍二、Prometheus组件与监控三、Prometheus基本使用&#xff1a;怎么来监控四、Kubernetes监控指标五、Prometheus的基本使用&#xff1a;部署1.基于docker部署prometheusgrafana2. 查看prometheus配置文件3. 监控Linux服务器3.1找到自己相应的系统去…

php外卖霸王餐独立小程序 霸王餐源码 功能用户分销 会员系统 自用/推广私域运营

前言 外卖霸王餐小程序是一种结合了外卖点餐和优惠返利功能的微信小程序&#xff0c;旨在为用户提供一种便捷的点餐体验和优惠福利。 用户以5-10元吃到原价15-25元的外卖&#xff0c;底层逻辑是帮外卖商家做店铺推广&#xff0c;解决新店基础销量、老店增加单量、品牌打万单店…