当前位置: 首页 > news >正文

【多线程】线程的五种创建方法

文章目录

  • 线程
  • 在 Java 代码中编写多线程程序
    • Thread 标准库
  • 创建线程的写法
    • 1 . 继承 Thread 类
      • 代码
      • 回调函数
      • 休眠操作:`sleep()`
      • 抢占式执行
      • 观察线程
        • jconsole
        • IDEA 内置调试器
    • 2 . 实现 Runnable 接口
      • 代码
    • 3. 匿名内部类创建 Thread ⼦类对象
      • 代码
      • 匿名内部类
    • 4.匿名内部类创建Runnable⼦类对象
      • 代码
    • 5.lambda 表达式创建 Runnable ⼦类对象

线程

  • 可以理解成更轻量的进程,也能解决[[01 计算机是如何工作的#^87b85a|并发编程]]的问题,但是创建/销毁的开销,要比进程更低

    • 因此多线程编程就成了当下主流的并发编程方式

    • 这个基本上是我们以后工作中天天用到,面试中考查的重点
      #高频面试

    • 在系统中,线程同样是通过 [[01 计算机是如何工作的#^7eb7b0|PCB]] 来描述的(Linux)

      • 一个进程,是一组 PCB
      • 一个线程,是一个 PCB
    • 一个进程中可以包含多个线程

      • 此时每个线程都可以独立的去 CPU 上调度执行

        • 线程是系统“调度执行”的基本单位
        • 进程是系统“资源分配”的基本单位
      • 一个可执行程序运行的时候(双击)

        • 操作系统会创建进程,给这个程序分配各种系统资源(CPU,内存,硬盘,网络带宽…)
        • 同时也会在这个进程中创建一个或多个线程,这些线程再去 CPU 上调度执行
      • 同一个进程中的线程,共用同一份资源

    • 线程比进程更轻量主要体现在可以资源共用

      1. 创建线程,省去了“分配资源”的过程
      2. 销毁进程,省去了“释放资源”的过程
      • 一旦创建进程,同时也会创建第一个线程==>分配资源,时间相对来说较慢

        • 一旦后续创建第二个、三个线程,就不必再重新分配资源了,用创建第一个线程时分配的资源
      • 当线程数目越来越多之后,此时效率也没办法进一步提升了(桌子的空间是有限的),当滑稽老铁数目达到一定程度后,有些人就够不到桌子了,就吃不到了
        62be5ee9b54355070b636fdcd9e0d3f.jpg|227

        • 能够提升效率,关键是充分利用多核心进行“并行执行

        • 如果只是“微观并发”,速度是没有提升的,真正能提升速度的是“并行

        • 如果线程数目太多,比如超出了 CPU 核心数目,此时就无法在微观上完成所有线程的“并行执行”,势必会存在严重的竞争

        • 当线程多了之后,此时就容易发生“冲突”

        • 由于多个线程,使用的是同一份资源(内存资源

        • 若多个线程针对同一个变量进行读写操作(尤其是写操作),就容易发生冲突

        • 一旦发生冲突,就可能使程序出现问题==>“线程安全问题

        • 一旦某个线程抛出异常,这个时候,如果不能妥善处理,就可能导致整个进程都崩溃,因此其他线程就会随之崩溃



  • 进程和线程的概念与区别(高频面试题,操作系统话题下出场频率最高的问题
    #高频面试

    1. 进程包含线程
      • 一个进程里面可以有多个线程
    2. 进程是系统资源分配的基本单位
      线程是系统调度执行的基本单位
    3. 同一个进程的线程之间,共用一份系统资源(内存,硬盘,网络带宽等)
      • 尤其是“内存资源”,就是代码中定义的变量/对象…
      • 编程中,多个线程,是可以共用一份变量的
    4. 线程是当下实现并发编程的主流方式,通过多线程,就可以充分利用好多核 CPU
      • 但也不是线程数越多就一定越好,当线程数达到一定的程度,把多个核心都利用充分之后,再增加线程,就无法再提高效率了,甚至可能会影响效率(线程调度也是有开销的)
    5. 多个线程之间可能会相互影响
      • 线程安全问题:一个线程抛出异常,也可能会把其他线程也一起带走
    6. 多个进程之间一般不会相互影响
      • 进程的隔离型:一个进程崩溃了,不会影响其他进程

在 Java 代码中编写多线程程序

  • 线程本身是操作系统提供的概念,操作系统提供 API 供程序猿调用
    • 但不同的系统,提供的 API 是不同的(Windows 创建线程的 API 和 Linux 的差别非常大)
    • Java(JVM)把这些系统 API 封装好了,咱们不需要关注系统原生 API,只需要了解好 Java 提供的这一套 API 就好了

Thread 标准库

  • 这个类负责完成多线程的相关开发

创建线程的写法

1 . 继承 Thread 类

代码

package thread;  class MyThread extends Thread {  //继承Thread类的目的是重写里面的run()方法  @Override  public void run() {  //这里写的代码就是即将创建的线程所需要执行的逻辑  while (true) {  System.out.println("hello thread");  //休眠操作,避免CPU消耗过大  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new RuntimeException(e);  }  }}
}  public class Demo1 {  public static void main(String[] args) {  MyThread t = new MyThread();  //创建线程,与主线程各自独立,并发执行  t.start();  //t.run(); 不会创建新线程,在主线程中执行,但执行不到//因为由于不是线程,所以不会并发执行,所以一直执行创建的MyThread线程,死循环while(true) {  System.out.println("hello main");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new RuntimeException(e);  }  }}  
}

这个代码运行起来是一个进程,但包含了两个线程

主线程——>main 方法(每个进程必有)
自创建的新线程——>t.start()

随后主线程和新线程就会并发/并行的在 CPU 上执行

  1. 创建一个类,继承于标准库的 Thread,并重写 run() 方法

    • Thread 类可以直接被继承,因为它来自 java. lang 这个包,而这个包是默认导入的,里面所有的类都可以直接使用
    • 继承 Thread 类的主要目的是重写 run() 方法
    • run() 中的方法就是即将创建出的线程要执行的逻辑
    1. main 方法里创建刚才那个类的实例,再使用提供的 start() 方法来创建线程

      • 调用 run() 就会在进程内部创建出一个新的线程,新的线程就会执行刚才 run 里面的代码
      • 线程明细:
        1. 主线程:调用 main 函数的方法需要一个专门的线程来执行,称为主线程
        2. t1.start();:这是创建的一个新进程,与主线程之间是并发/并行关系在 CPU 上执行
          • 这里调用 start() 是创建了一个新的线程,随后执行新线程里面的逻辑,而直接调用 run() 方法的话不会创建新的线程
    2. 运行结果 image.png|107


回调函数

非常重要的概念

  • run() 方法并没有被手动调用,但是最终也执行了,
  • 这种被用户定义了,但没手动调用的方法,最终是被系统/库/框架调用了,此时这样的方法就叫“回调函数(callback)
    • Java 数据结构中,优先级队列(堆),必须先定义好对象的“比较规则”
    • Comparable==>compareTo
      comparator==>compare
      都是自己定义了,但没有调用,此时都是由标准库本身内部的逻辑负责调用的

休眠操作:sleep()

  • 可以让循环每循环一次就休息一下,避免 CPU 消耗过大。单位是 ms(毫秒),1000 ms = 1 s
  • 这是一个静态方法类方法
    • 可以直接通过类名进行访问 类名. 方法名,不需要实例化对象,通过对象来访问

    • 这里会报错,使用 Alt+Enter

      • IDEA 自动生成 try/catch,catch 中默认的代码有两种风格

        1. 再次抛出一个异常:throw new RuntimeException(e);
        2. 只是打印异常信息:e.printStackTrace();
        • 但实际开发中不止这俩
          • 可能进行“重试
          • 可能进行“回滚
          • 可能会通过短信/邮件/微信/电话向程序猿报警

抢占式执行

  • 多个线程之间,谁先去 CPU 上调度执行,这个过程是“不确定的”,这个调度顺序取决于内核里面的“调度器”的实现
    • 调度器里面有一套规则,但是我们作为程序开发没法进行干预,也感受不到,只能把这个过程近似于随机

观察线程

jconsole
  • 可以借助第三方工具来看这两个进程的情况
    • JDK 中的 bin 目录(binary 二进制,里面放的都是可执行程序)
      • 通过这个可以看到 Java 中进程运行情况
        image.png|277
      • 远程进程:其他机器上的进程,需要通过网络连接
      • 本地进程:正在运行的进程
        • 一个 Java 线程中,不仅仅只有一个线程,其实有很多
        • 代码中自己创建的线程命名的规律是 Thread-数字
          image.png|202
      • 主要的线程(主线程和自己创建的线程)
        • 调用栈(非常有用)
          当代码出现问题,抛出异常,进程终止时,可以查看对应的调用栈找到出现问题的语句,以及这个代码是如何一层一层被调用过去的 image.png|398
      • 其他进程:主要起到辅助作用 image.png|156
        1. 垃圾回收:在合适的时机,释放不使用的对象
        2. 统计信息/调试信息:比如现在通过 jconsole 能查看到一个 Java 进程的详情
IDEA 内置调试器
  • 通过 IDEA 内置的调试器,也能看到类似的信息 image.png|313

2 . 实现 Runnable 接口

代码

package thread;  //通过Runnable的方式来创建线程  
class MyRunnable implements Runnable {  @Override  public void run() {  //描述线程要完成的逻辑  while (true) {  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new RuntimeException(e);  }        }    }
}  public class Demo2 {  public static void main(String[] args) throws InterruptedException {  MyRunnable runnable = new MyRunnable();  Thread thread = new Thread(runnable);  //通过Thread创建线程,线程要执行的任务是通过Runnable来描述的,不是通过Thread自己thread.start();  while (true) {  System.out.println("hello main");  Thread.sleep(1000);  }}
}

因为 Runnable 是一个 interface(接口),所以要用 implements(实现)

Runnable 是用来描述“要执行的任务”是什么
通过 Thread 创建线程,线程要执行的任务是通过 Runnable 来描述的,不是通过 Thread 自己来描述的

两种本质上差别不大,第二种更利于“解耦和
这个 Runnable 只是一个任务,并不与“线程”这样的概念强相关
后续执行这个任务的载体可以是线程,也可以是其他的东西

[!quote] 协程纤程

  • 线程是轻量级进程,但进程很重
  • 随着对性能要求的提高,开始嫌弃线程,引入协程
  • 也叫做虚拟线程

3. 匿名内部类创建 Thread ⼦类对象

本质是继承 Thread,和 1 一样

代码

package thread;  public class Demo3 {  public static void main(String[] args) throws InterruptedException {  Thread thread = new Thread() {  //这里就是在定义匿名内部类,这个类是Thread的子类  public void run() {  //在类内部重写run方法  while (true) {  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new RuntimeException(e);  }                }           }        };        thread.start();  while (true) {  System.out.println("hello main");  Thread.sleep(1000);  }    }
}

匿名内部类

[!quote] 匿名内部类

  • 一般是一次性的,用完就丢了
  • 内聚性更好一些
    • 相关联的代码放的越集中,内聚性越好
Thread thread = new Thread() {  //这里就是在定义匿名内部类,这个类是Thread的子类  public void run() {  //在类内部重写run方法   }
};
  • 这一段代码的解释
    1. 定义匿名内部类,这个类是 Thread 的子类
    2. 类的内部,重写了父类的 run 方法
    3. 创建了一个子类的实例,并且把实例的引用复制给了 thread

4.匿名内部类创建Runnable⼦类对象

本质是通过匿名内部类实现

代码

package thread;  public class Demo4 {  public static void main(String[] args) throws InterruptedException {  Runnable runnable = new Runnable() {  @Override  public void run() {  while (true) {  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new RuntimeException(e);  }                }           }        };        Thread thread = new Thread(runnable);  thread.start();  while (true) {  System.out.println("hello main");  Thread.sleep(1000);  }    }
}

5.lambda 表达式创建 Runnable ⼦类对象

  • 本质上就是匿名内部类,是一个更简化的写法
  • 很多时候,写“匿名内部类”,目的不是写“类”,而是为了写那个 run() 方法
  • lambda 可以直接表示我们要写的 run() 方法,省去了一些不需要的部分
public static void main(String[] args) {  Thread thread = new Thread(() -> {  while(true){  System.out.println("hello thread");  try{  Thread.sleep(1000);  }catch(InterruptedException e){  throw new RuntimeException(e);  }        }    });    thread.start();  while(true){  System.out.println("hello main");  try{  Thread.sleep(1000);  }catch(InterruptedException e){  throw new RuntimeException(e);  }  }
}

相关文章:

【多线程】线程的五种创建方法

文章目录 线程在 Java 代码中编写多线程程序Thread 标准库 创建线程的写法1 . 继承 Thread 类代码回调函数休眠操作:sleep()抢占式执行观察线程jconsoleIDEA 内置调试器 2 . 实现 Runnable 接口代码 3. 匿名内部类创建 Thread ⼦类对象代码匿名内部类 4.匿名内部类创…...

关闭窗口工具类 - C#小函数类推荐

此文记录的是一个关于关闭窗口工具类。 /***关闭窗口工具类Austin Liu 刘恒辉Project Manager and Software DesignerE-Mail: lzhdim163.comBlog: http://lzhdim.cnblogs.comDate: 2024-01-15 15:18:00使用方法:CloseWindowUtil.CloseWindow(this.Handle);***/n…...

Xilinx FPGA 原语解析(一):IBUFDS_GTE3 差分时钟输入缓冲器

目录 1.使用说明 2.实例化代码 3.参数解释 4.端口连接 1.使用说明 IBUFDS_GTE3 是Xilinx FPGA 中用于高速接口的差分时钟信号输入缓冲器。 BUFDS_GTEx,x2/3/4(不同系列的FPGA x的值不同),其中UltraScale使IBUFDS_GTE3…...

力扣SQL50 患某种疾病的患者 正则表达式

Problem: 1527. 患某种疾病的患者 在SQL查询中,REGEXP 是用于执行正则表达式匹配的操作符。正则表达式允许使用特殊字符和模式来匹配字符串中的特定文本。具体到你的查询,^DIAB1|\\sDIAB1 是一个正则表达式,它使用了一些特殊的通配符和符号。…...

k8s集群的资源发布方式(滚动/蓝绿/灰度发布)及声明式管理方法

目录 1.常见的发布方式 2.滚动发布 3.蓝绿发布 4.实现金丝雀发布(Canary Release) 5.声明式管理方法 1.常见的发布方式 蓝绿发布:两套环境交替升级,旧版本保留一定时间便于回滚优点:用户无感知,部署和回滚速度较…...

SwiftUI 中掌握 ScrollView 的使用:滚动可见性

文章目录 前言视图修饰符应用场景可见性完整示例ContentViewVideoPlayerViewScrollViewVisibilityApp 总结 前言 我们的滚动 API 中又有一个重要的新增功能:滚动可见性。现在,你可以获取可见标识符列表,或者快速检查并监控 ScrollView 内视图…...

中药养发护发

按照中医理论,头发和肝肾有密切联系,肝主血,肾藏精, 其华在发,肝肾强健,上荣于头,则毛发乌黑浓密. 中药育发的应用 以当归,天麻,桑疹子养血润发,配合干姜祛风活血,能通畅经络, 加快循环,激活毛囊,能促进皮肤组织营养成分吸收和废弃物的排泄,改善 头发生态. 用苦参 皂角 清热化…...

Java面试题-集合类

目录 1、请简单介绍下 Java 的集合类吧。 Collection Set TreeSet和HashSet List ArrayList 和 LinkedList 数组和链表的区别 Java 的列表有哪些实现类? Vector Queue Map 能说下 HashMap 的实现原理吗? 能说下 HashMap 的扩容机制吗&#x…...

【Vue3】组件通信之v-model

【Vue3】组件通信之v-model 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的…...

【Golang 面试 - 进阶题】每日 3 题(二)

✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...

Java中等题-多数元素2(力扣)【摩尔投票升级版】

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 示例 1: 输入:nums [3,2,3] 输出:[3] 示例 2: 输入:nums [1] 输出:[1]示例 3: 输入:num…...

100条超牛的DOS命令

目录 1. 文件和目录管理 1.1 列出文件和目录 1.1.1 dir 1.1.2 dir /w 1.2 切换目录 1.2.1 cd 1.2.2 cd .. 1.3 创建和删除目录 1.3.1 md / mkdir 1.3.2 rd / rmdir 1.4 文件操作 1.4.1 del / erase 1.4.2 copy 1.5 文件重命名 1.5.1 ren / rename 1.5.2 move …...

大数据信用报告查询会不会留下查询记录?怎么选择查询平台?

最近有不少网友都在咨询一个问题,那就是大数据信用报告查询会不会留下查询记录,会不会对自己的征信产生影响,下面本文就详细为大家介绍一下,希望对你了解大数据信用有帮助。 首先、大数据信用与人行征信是独立的 很多人只知道人行…...

JS【详解】内存泄漏(含泄漏场景、避免方案、检测方法),垃圾回收 GC (含引用计数、标记清除、标记整理、分代式垃圾回收)

内存泄漏 在执行一个长期运行的应用程序时,应用程序分配的内存没有被释放,导致可用内存逐渐减少,最终可能导致浏览器崩溃或者应用性能严重下降的情况,即 JS 内存泄漏 可能导致内存泄漏的场景 不断创建全局变量未及时清理的闭包&…...

第三期书生大模型实战营之Llamaindex RAG实践

基础任务 任务要求:基于 LlamaIndex 构建自己的 RAG 知识库,寻找一个问题 A 在使用 LlamaIndex 之前InternLM2-Chat-1.8B模型不会回答,借助 LlamaIndex 后 InternLM2-Chat-1.8B 模型具备回答 A 的能力,截图保存。 streamlit界面…...

【从0到1进阶Redis】Jedis 理解事务

笔记内容来自B站博主《遇见狂神说》:Redis视频链接 小伙伴们可以熟悉一下本专栏的 Redis 文章,可以更好地理解 正常操作 package oldfe.study;import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.T…...

MySQL之Lost connection to MySQL server during query复现测试

测试Lost connection to MySQL server during query复现条件 环境报错信息复现测试方式一方式二 环境 Python: 3.8/3.9 Mysql: 5.x 报错信息 File "/Users/xxx/lib/python3.9/site-packages/sqlalchemy/dialects/mysql/base.py", line 2509, in do_rollbackdbapi_con…...

中国AI大模型场景探索及产业应用调研报告

AI大模型发展态势 定义 AI大模型是指在机器学习和深度学习领域中,采用大规模参数(至少在一亿个以上)的神经网络模型,AI大模型在训练过程中需要使用大量的算力和高质量的数据资源。 产业规模 2023年,中国大模型市场规模为147亿。结合《202…...

Linux--shell脚本语言—/—<1>

一、shell简介 Shell是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构&am…...

【java框架开发技术点】通过反射机制调用类中的私有或受保护的方法

示例 假设我们有一个类 ExampleClass,其中有一个私有方法 privateMethod: public class ExampleClass {private void privateMethod(String message) {System.out.println("Private method called with message: " + message);} }我们可以使用上述代码来调用这个…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

AI,如何重构理解、匹配与决策?

AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

CSS | transition 和 transform的用处和区别

省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...