首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【javaEE】多线程--认识线程、多线程

【javaEE】多线程--认识线程、多线程

作者头像
那我掉的头发算什么
发布2026-01-12 19:01:57
发布2026-01-12 19:01:57
590
举报
这里是@那我掉的头发算什么 刷到我,你的博客算是养成了😁😁😁

前言

上文@计算机是如何运行的的最后我们谈到,进程由于创建和销毁的开销很大,频繁的创建和销毁会产生很多不必要的输出,所以,我们引入线程,线程类似于轻量级的进程,创建和销毁的开销都比较小。


线程

进程包含线程

每一个进程都包含一个或者多个线程

在我们之前java学习的过程中,使用的都是单进程单线程模型,涉及的都是单个进程。这个单线程就是main线程,具体的我们后面解释。

进程是操作系统资源分配的基本单位

进程的创建和销毁会涉及到资源的申请与释放,这个过程的开销很大。 而对于线程来说,只有创建第一个线程时会涉及到资源的申请,只有最后一个线程被销毁才涉及到资源的释放。而且在同一个进程内部管辖的多个线程之间共享所有的资源。 进程与进程之间的资源是各自独立不共享的。

线程是CPU上调度执行的基本单位

我们在上节学到,进程之间如果想要并行执行,需要进行进程调度。同样,线程在运行时也会产生相关的问题,也需要线程调度。 如果不能协调好线程内部运行的逻辑问题,就会产生“线程安全问题”。

知识回溯: 还记得我们之前学习stringbuffer和stringbuilder的时候提到过,stringbuffer是线程安全的,stringbuilder是线程不安全的。这里涉及的线程安全与本文提到的线程安全是一个问题。

上文我们提到过,线程中存在着以下的基本信息:

进程状态:如运行态(正在占用 CPU)、就绪态(等待 CPU)、阻塞态(等待资源 / 事件)。 上下文数据:进程切换前的 CPU 寄存器值、程序计数器(下一条要执行的指令地址)等。 进程优先级:不同进程因为需求不同有着不同的优先级。 进程的记账信息:统计每个进程在CPU上运行了多久,如果一个进程长时间阻塞可能考虑给这个进程一些资源让它运行。

线程中也类似存在着这样的信息,这一个进程中有多少个线程,就存放着多少个这样的信息,但是这些线程共同用一个内存指针和文件描述符表。

多线程

假设有两个房间,里面有两个人,他们同时在吃两只鸡:

在这里插入图片描述
在这里插入图片描述

这样的吃鸡效率很高,而且不会有抢食物的问题存在。 但是多一个房间意味着更大的开销,会产生资源浪费。

在这里插入图片描述
在这里插入图片描述

如果我们把鸡和人都装到一个房间里,省了空间的同时效率仍然很高。

现在我们假设有100只鸡,两个人来吃,一般来说不会产生争抢的问题。 但是如果我们增加人数嘞?

在这里插入图片描述
在这里插入图片描述

如果是100个人吃两只鸡,肯定分不过来,那么如果有的人没吃到鸡,直接生气不吃了,掀桌子,就会产生“异常”,如果不及时处理异常,程序就会崩溃。这样就会产生线程安全问题。

虽然提高线程的数目确实可以提高线程的效率,但是有“度”。 线程的数目不能无休止的增加,线程的数目达到一定程度之后,就算线程再多,也无法起作用相反甚至可能降低效率,因为会增加线程调度的开销。

进程与线程的区别⭐

定义本质 进程:操作系统资源分配的基本单位(比如内存、文件句柄)。 线程:进程内的执行调度基本单位(实际干活的 “执行流”)。 资源占用 进程:有独立的地址空间、内存等资源,进程间资源不共享。 线程:共享所属进程的所有资源(同一进程内的线程共用内存、文件等)。 开销成本 进程切换:开销大(需切换整个资源环境)。 线程切换:开销小(仅切换执行上下文,资源不用换)。 独立性与稳定性 进程:崩溃通常不影响其他进程(“隔离性强”)。 线程:崩溃会导致整个进程崩溃(因为共享资源)。

java代码实现多线程

api

api又叫应用程序编程接口,它是一套预先定义好的 “规则 / 工具”,让不同软件、组件能互相调用功能,不用关心对方内部是怎么实现的。大白话讲可以理解为别人写的一些函数/类,你直接拿来就用。 广义概念:标准库,第三方库,就算是你的同学你的同事提供的一段代码,你调用了也算。

在这里插入图片描述
在这里插入图片描述

api的作用就是方便编程。

创建线程

操作系统提供的原生线程api是c语言的,不同操作系统提供的线程api,不一样。在java中,统一将线程封装成一个类Thread类(标准库提供),使用时无需导入,因为实在java.lang包下的类,而java.lang包是默认导入的。

在这里插入图片描述
在这里插入图片描述

先不看源码,Thread里面有一个run方法,这个方法在Thread类中其实是一个空方法,需要自己重写定义。run方法相当于一个任务,我们在run中定义任务,然后启动线程就可以执行任务。

在这里插入图片描述
在这里插入图片描述

我们直接用t对象调用run方法,运行结果heloo Thread没问题。 但是,我们使用t.start();结果会是什么呢?

在这里插入图片描述
在这里插入图片描述

结果和run方法一样。 其实start方法是真正的创建了一个新的线程,线程在执行过程中执行了run任务,以此输出结果。 虽然run和start输出结果相同,但是内部逻辑有很大不同。

多线程

我前面说,main也是一个线程,如果这两个线程同时在执行一段很长的程序,在执行过程中,这两个线程会不会相互影响呢?

在这里插入图片描述
在这里插入图片描述

这段代码的运行结果很出乎预料,按照常理来说,似乎应该thread的循环一直执行才对,但是实际运行中,main和thread却是交替运行的。 由于对计算机来说,运行速度太快了,其实我运行这段代码时,输出窗口滑动飞快,我们可以使用一些办法来降低一下运行速度,以便更好地观察抢占过程。

在这里插入图片描述
在这里插入图片描述

sleep需要使用类名调用,是一个静态方法,它的作用是让线程休眠xxx毫秒。但是这个方法使用时会有编译期异常

知识回溯: 编译期异常== 受查异常 运行时异常== 非受查异常 我们处理异常时可以使用throws声明异常,或者try catch处理异常。

在main函数里,这两种方法都可以消除报错,但是Thread里面不可以用throws处理。因为:

Java 要求:子类重写父类方法时,声明抛出的 “检查型异常(Checked Exception)”,不能比父类方法声明的更宽泛。 原因: 当调用者使用 “父类引用” 调用方法时(这是 Java 多态的常见场景),调用者只会根据 “父类方法的异常声明” 来处理异常。如果子类方法抛出了 “更宽泛” 的检查型异常,而调用者没处理,就会导致编译错误(因为检查型异常必须显式处理),破坏了代码的兼容性。 如果父类方法没声明任何检查型异常,说明 “调用者无需处理任何检查型异常”。此时子类重写方法也不能声明任何检查型异常(因为 “无异常” 是最狭窄的范围,任何检查型异常都比它宽泛)

此处main方法可以随便throw抛异常处理,MyThread类因为父类没有抛异常只能使用try catch处理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们可以看到,hello thread和hello main并非按照严格先后顺序执行,因为线程的调度顺序在多线程时是随机的,本质上是一种“抢占式执行”的逻辑。

在这里插入图片描述
在这里插入图片描述

修改代码后,结果变化的原因: t.run()只是单纯的调用run方法使用,没有产生新的线程,本质上只有一个main线程,所以不会发生抢占。

第三方工具可视化多线程

在这里插入图片描述
在这里插入图片描述

我们在jdk目录下的bin文件夹里面(每个人的jdk目录可能不一样) 找到这样一个程序:

在这里插入图片描述
在这里插入图片描述

进去之后点击线程:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这之中大部分都是内置线程,启动任何线程都会同时启动这些线程。 这里只有我们手动启动的main线程一个。

在这里插入图片描述
在这里插入图片描述

通过这个可以看到线程运行的情况,到哪一步了。 下面我们注释掉run(),运行t.start():

就能看到我们启动创建的Thread-0了。

总结

本文围绕 Java 多线程展开,先明确进程是操作系统资源分配的基本单位、线程是 CPU 调度的基本单位,线程共享所属进程资源且创建销毁开销更小,同时对比了进程与线程在资源占用、开销、稳定性等方面的核心差异;接着介绍了 Java 通过标准库 Thread 类实现多线程的方式,强调重写 run 方法定义任务、调用 start 方法才是真正启动新线程(直接调用 run 仅为普通方法执行),并通过代码示例展示了多线程的抢占式执行特性;还补充了 API 的概念、线程休眠时的异常处理规则(子类重写父类方法不能抛更宽泛的受查异常),以及使用 jconsole 工具可视化查看线程的方法,完整呈现了多线程的基础核心知识。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 这里是@那我掉的头发算什么 刷到我,你的博客算是养成了😁😁😁
  • 前言
  • 线程
    • 进程是操作系统资源分配的基本单位
    • 线程是CPU上调度执行的基本单位
  • 多线程
  • 进程与线程的区别⭐
  • java代码实现多线程
    • api
    • 创建线程
    • 多线程
    • 第三方工具可视化多线程
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档