news 2026/3/19 3:39:42

通俗易懂讲线程--适合小白的零基础教程(面试版)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
通俗易懂讲线程--适合小白的零基础教程(面试版)

作者有话要说:

本文章适合想要快速了解和学习线程的基本知识点,适合考试复习和准备面试的同学。

一,了解线程和进程

官方给出的关于这两个名词的具体概念解释如下:

进程(Process)是系统进行资源分配的基本单位。

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

让我们拿一个工厂进行举例,来具体理解什么是资源分配,什么是运算调度。进程就像是工厂本身,而线程是工厂中的工人,操作系统就是工厂中的领导,CPU就是工厂中的机器

资源分配就是操作系统把计算机的硬件、软件资源,分配给进程使用的过程。

把操作系统比作 “工厂领导”,进程是 “工厂”,资源分配就是:

  • 工厂领导给每个工厂分配独立的办公室(内存)、办公设备(硬件);

运算调度本质就是给线程分配 CPU 的 “使用权”。

把 CPU 核心比作 “工厂的机器”,线程是 “操作机床的工人”,运算调度就是 “车间调度员” 的工作:

  • 决定哪一个工人(线程)在什么时间段使用这个机器(CPU)。

具体关系图如下如所示(请忽略丑陋的画技):

二,创建线程的几种方式

一般说到实现线程的几种方法,大家都会想到四种方式:继承Thread类,实现callable接口,实现runnable接口,线程池。实现两种接口的方法是一样的,所以这边咱们只介绍实现runnable接口的方式,至于两种接口方式实现的区别和优缺点,这里不做介绍,未来会单独出一期内容介绍;还有线程池实现线程的方式,线程池是一个很大的知识点,需要用的技术更加复杂,所以也放到以后单独的一期内容去讲解。

接下来的代码学习建议大家可以自己动手敲一遍代码感受一下,能够帮助大家理解。

先给大家介绍一下通过继承Thread类实现线程的代码:

public class ThreadEasyA extends Thread{ //通过继承Thread类实现线程 @Override public void run() { System.out.println("线程运行"); } public static void main(String[] args) { ThreadEasyA a = new ThreadEasyA(); a.start(); } }

运行结果:线程运行

还有通过实现接口创建线程的方式:

public class RunableThread implements Runnable{ //通过实现runnable接口实现线程 @Override public void run() { System.out.println("线程运行"); } public static void main(String[] args) { Thread t = new Thread(new RunableThread()); t.start(); } }

运行结果:线程运行

三,知识拓展(可跳过):

这一部分的知识点与线程无关,不想要学习的同学可以直接跳过。那为什么要介绍这两个概念呢?因为在平时的代码书写中,通过匿名内部类的方式和lambda表达式的方式去写创建线程的代码可以更加的简洁,而且线程的代码是很经典的使用匿名内部类和lambda表达式的教学案例。

匿名内部类

首先什么叫做匿名内部类呢:

匿名内部类是 Java 中一种没有显式类名的局部内部类,它是定义类、创建实例的 “一站式” 语法糖 —— 直接继承某个类 / 实现某个接口,并且在定义的同时就创建该类的唯一实例。它的核心价值是简化代码,尤其适用于仅需使用一次的类实例场景,避免为简单逻辑单独定义命名类。

匿名内部类的特点:

  • 没有类名。
  • 必须继承类或者实现接口。
  • 可访问外部类对象。
  • 没有构造方法。

到这里还没有理解的同学也没有关系,接下来通过对比代码的形式来帮助大家去理解什么叫做匿名内部类:

public class ThreadEasy { public static void main(String[] args) { // 匿名内部类实现线程 Thread t = new Thread(new Runnable(){ @Override public void run() { System.out.println("匿名内部类BBBB"); } }); t.start(); } }

从代码中可以看出来,普通的通过Thread类实现接口需要两步(定义类+创建实例),但是通过匿名内部类的方式只需要一步就可以实现

lambda表达式

这里的道理和上面是一样的,咱们直接上代码:

public class ThreadEasy { public static void main(String[] args) { //lambda表达式实现线程 new Thread(() -> System.out.println("lambda表达式")).start(); } }

大家可以看出来,是不是代码更加的简洁了,只需要一行代码就可以显示线程的创建。不过在使用lambda表达式的时候需要注意,lambda表达式只适用于函数式接口(只有一个抽象方法的接口)。

四,线程常用方法

接下来给大家介绍一下线程中常用的几种方法,按照每个方法的难度程度,将会给大家详细介绍一下join方法:

run()

run方法的作用:run方法并不是用来启动线程的,从上面的代码中大家可以看出来,不管是继承Thread类还是实现接口,都重写了run方法,但是run方法实际上并不会对线程造成什么影响,你可以将run方法看成是大门。大门将线程内部与外部隔绝。

start()

start方法的作用:就相当于线程的启动器,用于启动线程,也就是打开线程的大门,将里面的东西放出来

join()

join方法的作用:join方法的作用就相当于插队,在A线程中执行B.join()方法,就是让A线程进入停止状态(阻塞状态),让B线程先运行,等到B线程运行完毕,就可以重新开始运行A线程。

接下来通过对比代码来给大家展示一下join方法的效果:

第一种情况:没有join方法

public class ThreadEasyA extends Thread{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " is " + i); } } public static void main(String[] args) { ThreadEasyA a = new ThreadEasyA(); ThreadEasyA b = new ThreadEasyA(); a.start(); b.start(); System.out.println("This is main Thread! It is running!!"); } }

运行结果:大家可以尝试一下,不管运行几遍,This is main Thread!都在第一行,下面的Thread-0和Thread-1的顺序是变化的。(Thread-0就是线程a的执行结果,Thread-1就是线程b的执行结果)

也就是代码的执行顺序是先执行main,然后a线程和b线程交替执行。

This is main Thread! It is running!! Thread-0 is 0 Thread-0 is 1 Thread-1 is 0 Thread-1 is 1 Thread-0 is 2 Thread-1 is 2 Thread-1 is 3 Thread-0 is 3 Thread-1 is 4 Thread-0 is 4

第二种情况:线程a执行join方法

public class ThreadEasyA extends Thread{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " is " + i); } } public static void main(String[] args) throws InterruptedException { ThreadEasyA a = new ThreadEasyA(); ThreadEasyA b = new ThreadEasyA(); a.start(); a.join(); b.start(); System.out.println("This is main Thread! It is running!!"); } }

运行结果:可以看出来执行顺序是先执行a线程,再执行main,最后执行b线程

也就是说执行了a.join方法之后,mian被阻塞了,先让a线程插队执行,a线程执行完毕后,才放开main

Thread-0 is 0 Thread-0 is 1 Thread-0 is 2 Thread-0 is 3 Thread-0 is 4 This is main Thread! It is running!! Thread-1 is 0 Thread-1 is 1 Thread-1 is 2 Thread-1 is 3 Thread-1 is 4

大家可以自己再尝试一下其他的join组合来看一下运行的效果。

不过join方法容易造成堵塞的情况,例如,想象一下,在A线程中执行B.join,在B线程中执行A.join,那么AB线程都会等待对方先执行,那么这种情况下就造成了两个线程都没有办法运行。

yield()

yield方法的作用:yield方法就是告诉调度器(领导),当前线程(工人)愿意让出CPU资源(工厂机器)。但是线程的具体执行顺序还是有调度器来决定的。所以执行yield不一定会让当前线程停止。所以该方法适合用于让出CPU但是不阻塞线程的场景

wait()

wait方法的作用:让线程释放锁并进入等待状态。

notify()

notify方法的作用:唤醒正在等待的线程。一般情况下,wait方法和notify方法是一起使用的。

五,线程的状态

线程的状态图如下所示:

  • 新建状态 (New): 当通过 new Thread () 创建一个线程对象后,该线程处于新建状态,此时它尚未开始执行。
  • 就绪状态 (Runnable): 当调用线程对象 start () 方法之后,线程就进入就绪状态,处于这个状态的线程已经准备好运行,等待 CPU 的调度。
  • 运行状态 (Running): 如果线程获得了 CPU 时间片,开始执行 run () 方法的线程执行体,则线程处于运行状态。
  • 阻塞状态 (Blocked):线程在执行过程中,可能因为各种原因进入阻塞状态如执行了 sleep () 方法,或者等待某个同步锁。
  • 等待状态 (Waiting):线程在等待其他线程执行特定操作调用 wait ()、join ()、LockSupport.park () 方法进入等待状态;通过 notify ()、notifyAll ()、interrupt () 方法进入就绪状态。
  • 死亡状态 (Dead):当线程的 run ()、call () 方法执行完毕或者线程被中断,它将进入死亡状态。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 15:47:47

紧急修复检索偏差!:Dify重排序参数调优的4步快速响应方案

第一章&#xff1a;紧急修复检索偏差&#xff1a;Dify重排序参数调优的4步快速响应方案在使用 Dify 构建检索增强生成&#xff08;RAG&#xff09;应用时&#xff0c;检索结果的准确性直接影响最终输出质量。当出现检索偏差——即相关文档未能被有效排序至前列时&#xff0c;需…

作者头像 李华
网站建设 2026/3/15 19:55:23

【Docker MCP 网关服务注册全解析】:掌握微服务动态注册核心技术

第一章&#xff1a;Docker MCP 网关服务注册概述在微服务架构中&#xff0c;Docker MCP&#xff08;Microservice Control Plane&#xff09;网关承担着服务发现、路由转发与统一鉴权等核心职责。服务注册是其运行的前提&#xff0c;确保每个启动的微服务实例能够被正确识别并纳…

作者头像 李华
网站建设 2026/3/15 19:54:55

SpringBoot新手入门:从0到1快速搭建Web应用

一、为什么 Spring Boot 是新手的「Java 开发加速器」&#xff1f; 想象开发 Java Web 应用像组装家具&#xff1a; 传统 Spring&#xff1a;需手动筛选板材&#xff08;依赖&#xff09;、阅读厚厚说明书&#xff08;XML 配置&#xff09;、自备工具&#xff08;服务器&…

作者头像 李华
网站建设 2026/3/16 11:37:01

Docker Offload优先级配置揭秘:如何实现资源利用最大化?

第一章&#xff1a;Docker Offload优先级配置的核心概念Docker Offload机制允许将容器化工作负载从主执行路径卸载到专用处理单元&#xff08;如GPU、FPGA或协处理器&#xff09;&#xff0c;以提升系统性能与资源利用率。在复杂的多任务环境中&#xff0c;合理配置Offload优先…

作者头像 李华
网站建设 2026/3/15 19:54:57

多模态Agent Docker启动顺序深度解析(专家级部署方案曝光)

第一章&#xff1a;多模态Agent架构与Docker化挑战构建现代AI系统时&#xff0c;多模态Agent因其能够融合文本、图像、音频等多种数据类型而备受关注。这类Agent通常由多个异构组件构成&#xff0c;包括感知模块、推理引擎、动作生成器以及外部接口服务。将此类复杂架构容器化部…

作者头像 李华