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

多线程系列一:认识线程

1概念

1.1线程是什么?

一个线程就是一个执行流,每个线程之间都可以按照顺序执行自己的代码,多个线程之间同时进行着多份代码。

1.2为什么要有线程?

让并发编程成为刚需

  • 单核CPU的发展遇到了瓶颈,要想提高算力,就需要多核CPU,而并发编程能更充分利用多核CPU资源
  • 有些任务场景需要“等待IO”,为了让等待IO的时间能够去做一些其他工作,也需要用到并发编程
  • 虽然多进程也能实现并发编程,但是线程比进程更轻量

一个进程刚启动的时候首当其冲的是内存资源,进程要把依赖的代码和数据加载到内存中,从系统分配一个内存并不是一个容易的事情,一般来说,申请内存时要指定一个大小,系统内部就把各种大小的空间内存通过一定的数据结构组织起来,实际申请时,就要去这样的空间中进行查找,找到一个合适大小的内存分配过来,如果请求很多,就要频繁的创建和释放这样的操作,这样的操作,开销比较大,其中关键的原因是资源的申请和释放,进程是资源分配的基本单位
结论:进程在进行频繁创建和销毁时,开销比较大,主要体现在资源的申请和释放

线程就是上述问题的解决方案,线程也称为“轻量化进程”,在进程的基础上做出了改进,保持了独立调度执行,同时省去分配和释放资源带来的额外开销

1.3线程和进程的区别(高频面试题!!!)

  • 进程是包含线程的,每一个进程至少有一个线程存在,即主线程
  • 进程和进程之间不共享内存空间,同一个进程的线程之间共享同一个内存空间
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位
  • 每个线程是一个独立的执行流,可执行一些代码并单独的参与到CPU的调度中
  • 每个进程都有自己的资源,进程中的线程共用这一份资源
  • 一个进程挂了一般不会影响到其他进程,但是一个线程挂了,可能把同进程内的其他线程一起带走,造成整个进程崩溃

在这里插入图片描述

PCB中有个属性是内存指针,多个线程的PCB指针指向的是同一个内存空间,这样就意味着只创建第一个线程时要从系统分配,后续的线程就不必分配,直接从前面那份资源即可,此外,文件描述符表也是共用的,也不是随便两个线程就能共享资源,把能够资源共享的这些线程分成组,称为“线程组”,线程组也就是进程的一部分

1.4内核

操作系统的内核是最核心的功能模块(管理硬件,给软件提供稳定的运行环境)我们用的应用程序有时要针对一些系统提供的软硬件进行操作的,这些操作都不是应用程序直接操作的,就要调用系统的api进一步在内核中完成这样的操作,我们用的普通的应用程序都是运行在用户态的

为什么要划分用户态和内核态?
主要目的是为了稳定,防止应用程序把硬件或软件资源搞坏了,应用程序只能调用这些api,不至于对系统或硬件设备产生太大的危害

2第一个多线程程序

class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class demo {public static void main(String[] args) {MyThread t = new MyThread();t.start();while(true){System.out.println("hello main");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

说明:

  1. 真正运行程序,两个死循环都在执行并发线程,这两个线程就是两个独立的执行流
  2. Thread直接能使用,不用导包,创建一个自己的类,继承Thread,java标准库中有一个特殊的包:java.lang默认就被导入
  3. 一个进程中至少有一个线程,这个进程中的第一个线程被称为“主线程”,main方法就是主线程的入口方法
  4. 线程实例才是真正的线程
  5. 调用Thread的start方法,才是真正调用系统api在系统内核中创建出线程,线程就会执行上面的run方法
  6. 调用start创建线程后,兵分两路,一路沿着main,一路进入到线程的run方法,各搞各的,相互独立,但是先后顺序不确定
  7. 先后顺序不确定的解释:因为操作系统中有个叫调度器的模块,这个模块的实现方式是一种类似于”随机调度”的效果,随机调度也称为“抢占式执行”,给我们的线程安全问题埋下伏笔

我们可以使用jconsole命令观察线程
在这里插入图片描述

  • 在这里,Thread-0是自己创建的线程,main是主线程

3创建线程的机种方式

3.1继承Thread类

class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class demo {public static void main(String[] args) {MyThread t = new MyThread();t.start();while(true){System.out.println("hello main");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3.2匿名内部类创建Thread对象

Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("使用匿名内部类创建Thread对象");}};

写{}的意思是要定义一个类,与此同时,这个新的类继承自Thread方法,此处的大括号中可以定义子类的实例,t1指向的实例并非是单纯的Thread,而是Thread子类,但是我们不知道这个类叫啥,因为是匿名的

3.3匿名内部类创建Runnable子类对象

Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("使用匿名类创建Runnable子类对象");}});

3.4lambda表达式创建Runnable子类对象

public static void main(String[] args) {Thread t3 = new Thread(()->{System.out.println("使用匿名类创建Thread子类对象");});}

lambda代替了Runnable的位置

相关文章:

  • 【计算机网络】​TCP(传输控制协议)套接字,多线程远程执行命令编程​
  • 2025年真实面试问题汇总(一)
  • 【Python学习路线】零基础到项目实战
  • RFID光触发标签工业级分拣难题的深度解决方案
  • Vue3笔记摘录
  • 读论文笔记-CoOp:对CLIP的handcrafted改进
  • 兰亭妙微:全流程交互设计和设计前后对比
  • 如何加速机器学习模型训练:深入探讨与实用技巧
  • Vue2 vs Vue2.7 深度对比
  • 【Java】打印运行环境中某个类引用的jar版本路径
  • Nginx核心
  • 深入探索ChatClient:简化AI模型交互的强大工具
  • Compose笔记(二十一)--AnimationVisibility
  • 深度学习论文: Describe Anything: Detailed Localized Image and Video Captioning
  • 柔性生产是什么?怎样能实现柔性生产?
  • PC端实现微信扫码登录
  • 图数据库榜单网站
  • Doris索引机制全解析,如何用高效索引加速数据分析
  • ESP32开发-作为TCP服务端接收数据
  • Oracle Bigfile 与 Smallfile 表空间对比分析
  • 五一去哪儿| 追着花期去旅行,“赏花经济”绽放文旅新活力
  • 西夏文残碑等文物来沪,见证一段神秘灿烂的历史
  • 杨国荣︱学术上的立此存照——《故旧往事,欲说还休》读后
  • “五一”假期,又有多地将向社会开放政府机关食堂
  • 宣称防老年痴呆的“原装进口”保健品McPee被指涉假,未获澳方销售批准
  • 湖南省郴州市统战部部长黄峥嵘主动交代问题,接受审查调查