Java Thread 类基础用法完整总结 —— 线程创建、中断、等待、休眠、线程实例获取、分清为start/run,避开sleep坑点
目录1. 线程创建1.1 继承Thread类1.2 实现Runnable接口1.3 Lambda简化创建线程2. 线程中断2.1 interrupt() 标记中断2.2 isInterrupted() 判断中断标记2.3 interrupted() 清除中断标记3. 线程等待3.1 join() 等待线程执行完毕3.2 join(long) 限时等待4. 线程休眠4.1 Thread.sleep() 静态休眠方法4.2 sleep中断异常处理5. 获取线程实例5.1 Thread.currentThread() 获取当前线程5.2 线程名称、ID获取前言本文全面总结Java Thread类五大核心基础用法包含代码示例、底层原理与易错点适合Java线程入门学习。1. 线程创建1.1 继承Thread类Java中第一种创建线程的方式是通过继承,写法很简单只是Java不支持多继承,一旦继承Thread类之后无法继承多个类;方式一:通过继承并重写Thread中的方法classMyThreadextendsThread{Overridepublicvoidrun(){System.out.println(我是MyThread);}}publicclassdemo{publicstaticvoidmain(String[]args){MyThreadmyThreadnewMyThread();myThread.start();System.out.println(我是main);}}新建类继承Thread父类重写run()方法写入线程需要执行的代码创建自定义线程类对象调用对象start()方法启动线程我们启动线程调用的是start方法而不是我们重写的run方法此时,就代表我们成功开启并且顺利结束了子线程;当然面试的时候面试官也有可能会问你,为什么调用的是start方法而不是run方法?run()只是封装线程业务逻辑的普通成员方法不具备创建新线程的底层能力。如果手动调用线程对象.run()代码只会在当前主线程串行执行不存在多线程并发。start()是 Thread 类原生启动方法底层调用 native 本地方法完成两步核心操作向操作系统申请线程资源创建一条全新独立的执行线程操作系统调度就绪后JVM会自动回调执行该线程的run()方法。我们需要注意的是同一个线程的start方法只能调用一次,如果重复调用当我们执行的时候就会抛出异常;如:为了更好的理解,我们将代码修改成一个死循环classMyThreadextendsThread{Overridepublicvoidrun(){while(true){System.out.println(我是MyThread);}}}publicclassdemo{publicstaticvoidmain(String[]args){MyThreadmyThreadnewMyThread();myThread.run();while(true){System.out.println(我是main);}}}此时我们能看见只要一个数据在进行输出我们也可以去JavaJDK中bin目录下寻找jconsole.exe这一个可执行文件;我们从始至终都只看见一个main线程;因此,此时我们调用run方法是不会创建线程;在此基础上我们也可以采用匿名内部类的方式来进行创建线程publicclassdemo{publicstaticvoidmain(String[]args){ThreadthreadnewThread(){Overridepublicvoidrun(){while(true){System.out.println(我是MyThread);}}};thread.start();while(true){System.out.println(我是main);}}}这样创建也能达到相同的效果,并且简化了我们的代码; 返回目录1.2 实现Runnable接口Runnable是 Java 提供的函数式接口仅包含一个抽象方法void run()专门用于封装线程要执行的任务逻辑。相比继承Thread类实现Runnable是开发中推荐的线程创建方式。使用Runnable函数式接口更加解耦。classMyThreadimplementsRunnable{Overridepublicvoidrun(){while(true){System.out.println(我是MyThread);}}}publicclassdemo{publicstaticvoidmain(String[]args){ThreadthreadnewThread(newMyThread());thread.start();while(true){System.out.println(我是main);}}}自定义类implements Runnable重写run()方法写入线程业务逻辑创建自定义任务类实例将任务实例传入Thread构造器创建线程对象调用线程对象的start()启动线程自动执行 run 方法使用匿名内部类的方式publicclassdemo{publicstaticvoidmain(String[]args){ThreadthreadnewThread(newRunnable(){Overridepublicvoidrun(){while(true){System.out.println(我是MyThread);}}});thread.start();while(true){System.out.println(我是main);}}}这两种创建方式都能达到简化代码的效果,当然最简化代码的是Lambda表达式 返回目录1.3 Lambda简化创建线程使用Lambda表达式前置条件:函数式接口接口中仅有一个抽象方法无需关心接口名、方法名只需要关注方法入参、返回值Runnable.run()无参数、无返回值对应 Lambda 标准格式() - { 线程任务代码 }当我们{ }中只有一条语句的时候,{ }可以省略不写;publicclassdemo{publicstaticvoidmain(String[]args){ThreadthreadnewThread(()-{while(true){System.out.println(我是MyThread);}});thread.start();while(true){System.out.println(我是main);}}}仅支持函数式接口只能有一个抽象方法不能用于普通多抽象方法接口Lambda 内部访问局部变量时变量必须是final或有效 final不能二次修改)我们这样创建线程就会简便很多;当然在上述情况下我们创建的线程名字都是默认的我们可以在创建线程的时候在后面加上我们要给线程赋予的名字例如,我们想给线程名字起为:ThreadMain当线程创建好之后,我们能在jconsole中进行查看 返回目录2. 线程中断当一个线程执行到一半的时候,我们不想让这个线程执行到后面的代码,此时我们应该怎么办?Java官方并没有给我们提供强制终止线程的API,因为强制终止线程是一个非常危险的方法;我们可以使用中断标记协作式停止核心依靠三个方法interrupt()、isInterrupted()、Thread.interrupted()。 返回目录2.1 interrupt() 标记中断调用目标线程对象的interrupt()方法仅给线程设置中断标志位不会立刻停止线程只是发送 “请求中断” 的信号线程是否退出由自身业务逻辑决定。如:ThreadtnewThread(()-{while(true){// 业务循环}});t.start();t.interrupt();这段代码在执行的时候只会修改内部的标志位,而不会让代码主动停止下来;例子二:ThreadtnewThread(()-{try{Thread.sleep(1000);}catch(InterruptedExceptione){Thread.currentThread().interrupt();System.out.println(线程中断);}});t.start();t.interrupt();此时我们调用了Thread中sleep方法进行休眠1秒钟;在调用t.interrupt的时候,会将Thread内置的标志位从默认false修改为true当线程被中断时,会唤醒sleep,这个sleep非常的坑,后续我们会讲到;除了调用interrupt()由 JVM 内置中断标记外我们也可以自定义布尔变量作为中断标志实现手动控制线程停止。privatestaticvolatilebooleanisStopfalse;publicstaticvoidmain(String[]args){ThreadworknewThread(()-{while(!isStop){System.out.println(任务运行中);try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println(收到停止信号线程退出);});work.start();ScannerscannernewScanner(System.in);intascanner.nextInt();if(a0){isStoptrue;}}我们设置的是全局变量,那如果我们设置的是局部变量呢?Java 语法规则Lambda 表达式 / 匿名内部类访问方法内局部变量时局部变量必须是final或 effectively final有效 final。boolean isStop false;是 main 方法局部变量后续如果想在主线程修改isStop true;变量就不再是有效 final编译器直接报红报错哪怕你不修改这个变量仅读取语法上允许但无法外部手动修改标志位停止线程失去手动标记的意义。有效 final:变量初始化后没有任何重新赋值操作编译器自动视为有效 final允许 Lambda / 匿名内部类访问一旦出现变量 新值赋值语句直接破坏有效 final编译报错。 返回目录2.2 isInterrupted() 判断中断标记publicbooleanisInterrupted()isInterrupted作用:读取线程当前的中断标记状态读取后不会清除标记是业务循环中最常用的中断判断方式。publicstaticvoidmain(String[]args){ThreadtnewThread(()-{while(!Thread.currentThread().isInterrupted()){System.out.println(业务执行中...);}});t.start();ScannerscnewScanner(System.in);intasc.nextInt();if(a0){t.interrupt();}}此时当我们输入给a的值为0的时候,此时interrupt会将内置的标志位改为true,此时线程里面的循环就会停止下来;实例方法必须通过线程对象 /currentThread()调用只查询标记不修改、不清空中断状态适合长轮询、循环任务做退出判断 返回目录2.3 interrupted() 清除中断标记publicstaticbooleaninterrupted()作用:静态方法判断当前执行该方法的线程是否带有中断标记调用完成后自动清空中断标记。例如:我们先给当前线程打上标记位:publicstaticvoidmain(String[]args){// 给当前线程打上中断标记Thread.currentThread().interrupt();System.out.println(Thread.interrupted());System.out.println(Thread.interrupted());}适用场景: 当我们需要一次性消费中断信号消费后不再保留中断状态时使用大部分业务场景优先用isInterrupted()。 返回目录3. 线程等待3.1 join() 等待线程执行完毕有时候,我们并不一定是想要中断一个线程,而是等待一个线程执行完,在轮到这个线程进行执行;举个很常见的例子:在橘子汁加工厂中,假设只有榨汁,装瓶两个操作;我们想要一杯果汁,只有在榨汁完的前提之下,才能进行装瓶这一个操作;如果装瓶这一个操作等待榨汁这一个过程,就可能会出现问题;publicvoidjoin()作用:等待线程结束注意:在哪一个线程中调用对应的join()哪一个线程就会阻塞publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadjuicernewThread(()-{System.out.println(开始榨汁...);try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(榨汁完成);});juicer.start();juicer.join();System.out.println(开始装瓶);}这样才能保证,先榨汁后装瓶,因为操作系统的调用是随机的,我们不能保证操作系统先调用哪一个线程 返回目录3.2 join(long) 限时等待带超时时间的重载方法最多等待指定毫秒时长publicvoidjoin(longmillis)若目标线程在超时前执行完毕当前线程提前解除阻塞若等待时长耗尽目标线程仍未结束当前线程不再等待直接向下执行。再举一个例子:小萌和小宇一天晚上说好了一起去逛街,第二天小宇去小萌家小区门口等小萌,小宇最多等小萌3秒钟而小萌,吃早餐和打扮需要5秒钟,如果超时了,小宇就直接打电话了publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt1newThread(()-{System.out.println(开始打扮...);try{Thread.sleep(5000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(小萌打扮完成);});t1.start();//小宇开始等t1.join(3000);//此时小萌还没下来,小宇就直接上去找了System.out.println(小宇等不住了,直接去找小萌);}我们假设榨汁时间为5秒钟,但是装瓶的时候我们只等你3秒钟,3秒钟超时了,我就不等你了;join(long millis, int nanos)精确到纳秒级别的限时等待日常开发极少使用。 返回目录4. 线程休眠4.1 Thread.sleep() 静态休眠方法线程休眠用于让当前线程暂停指定时长让出CPU执行权进入阻塞状态核心API为静态方法Thread.sleep()public static void sleep(long millis)休眠指定毫秒public static void sleep(long millis, int nanos)毫秒 纳秒高精度休眠主要作用:静态方法作用于当前正在执行该代码的线程不是调用某个线程对象休眠阻塞状态休眠期间线程放弃 CPU 资源不参与调度不会释放锁若线程持有synchronized锁休眠时锁依旧占用其他线程无法获取示例:publicstaticvoidmain(String[]args){System.out.println(主线程开始执行);try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(3秒后唤醒);} 返回目录4.2 sleep中断异常处理这里的异常处理就非常的坑人了,线程处于sleep()休眠阻塞时若其他线程调用该线程的interrupt()会立刻抛出InterruptedException同时自动清空中断标记。也就是当我们提前中断线程的时候会将默认的标志位从false修改为true而当唤醒sleep的时候,会将线程中我们修改的标志位再从true改回为false这个操作就非常的坑人了,稍有不注意,就会出现线程安全问题;例如:publicstaticvoidmain(String[]args){ThreadthreadnewThread(()-{while(!Thread.currentThread().isInterrupted()){System.out.println(我是MyThread);try{Thread.sleep(1000);}catch(InterruptedExceptione){System.out.println(MyThread被中断了);}}});thread.start();ScannerscnewScanner(System.in);intasc.nextInt();if(a0){thread.interrupt();}}此时我们就发现就算我们想要提前中断当前线程,我们发现根本就没有用,线程中的死循环还是在执行. 返回目录5. 获取线程实例5.1 Thread.currentThread() 获取当前线程开发中经常需要拿到当前运行线程的对象用于操作线程名称、状态、中断标记等Thread类提供静态方法获取当前线程实例配套实例方法读取线程ID与名称。publicstaticThreadcurrentThread()静态方法返回正在执行该代码片段的线程对象与调用位置绑定在哪个线程中调用就返回哪一个线程的实例publicstaticvoidmain(String[]args){ThreadmainThreadThread.currentThread();System.out.println(主线程mainThread);}结果如下: 返回目录5.2 线程名称、ID获取方法功能说明long getId()获取线程唯一标识 IDJVM 自动分配不可修改String getName()获取线程名称默认格式Thread-数字void setName(String name)修改线程自定义名称便于日志区分publicstaticvoidmain(String[]args){ThreadtnewThread(()-{ThreadcurrThread.currentThread();// 获取ID与名称longidcurr.getId();Stringnamecurr.getName();System.out.printf(IDid名称name);},订单处理线程);t.start();System.out.println(外部读取线程名t.getName());}注意:线程 ID 是全局唯一数值线程销毁后不会复用 返回目录

相关新闻

RTX50系显卡性能对比与选购指南

RTX50系显卡性能对比与选购指南

1. 五款RTX50系显卡规格参数对比让我们先来看一下RTX5060到5070Ti五款显卡的核心规格差异:型号CUDA核心显存容量显存类型显存位宽加速频率TDP功耗首发价格RTX506038408GBGDDR6128-bit2.4GHz170W2499RTX5060 Ti486412GBGDDR6192-bit2.45GHz200W3299RTX5070614412GBGD…

2026/7/21 5:44:46阅读更多 →
SQL面试解题心法:从窗口函数到NULL安全的四大核心能力

SQL面试解题心法:从窗口函数到NULL安全的四大核心能力

1. 这不是题库搬运,而是一套可复用的SQL面试解题心法 你刷过几百道SQL题,但一到真实面试现场,面对白板或共享编辑器,手还是抖?写完代码自己都心虚——不确定窗口函数该用 RANK() 还是 DENSE_RANK() ,分…

2026/7/21 5:44:46阅读更多 →
多模态知识图谱在食品智能问答中的应用与优化

多模态知识图谱在食品智能问答中的应用与优化

1. 多模态知识图谱如何重塑食品领域智能问答去年我在开发一个健康饮食推荐系统时,曾遇到一个典型问题:当用户询问"糖尿病患者适合吃菠萝吗"这类复合型问题时,传统文本知识图谱只能给出"菠萝含糖量约10g/100g"这类碎片信息…

2026/7/21 5:42:42阅读更多 →
为什么选择command-line-args?深度解析5大核心优势与使用场景

为什么选择command-line-args?深度解析5大核心优势与使用场景

为什么选择command-line-args?深度解析5大核心优势与使用场景 【免费下载链接】command-line-args A mature, feature-complete library to parse command-line options. 项目地址: https://gitcode.com/gh_mirrors/co/command-line-args 在Node.js生态系统中…

2026/7/21 15:49:37阅读更多 →
深入解析C2000 eQEP模块:从正交编码器原理到精准运动控制实战

深入解析C2000 eQEP模块:从正交编码器原理到精准运动控制实战

1. 项目概述:从编码器脉冲到精准运动控制 在电机控制、机器人关节驱动、精密仪器定位这些领域,工程师们每天都要面对一个核心问题:如何精确地知道一个旋转或直线运动的物体“在哪里”以及“跑多快”。这听起来简单,但却是整个闭环…

2026/7/21 15:49:37阅读更多 →
TILER技术解析:二维平铺内存如何突破嵌入式图像处理的内存墙

TILER技术解析:二维平铺内存如何突破嵌入式图像处理的内存墙

1. 从线性存储到二维平铺:为什么我们需要TILER?如果你在嵌入式系统或者高性能计算领域折腾过图像处理,尤其是视频编解码,那你一定对“内存墙”这个词不陌生。处理器的算力在飞速增长,但内存带宽和访问延迟的改善却相对…

2026/7/21 15:49:37阅读更多 →
C 语言工业级通用组件手写 11:环形 FIFO 缓冲区

C 语言工业级通用组件手写 11:环形 FIFO 缓冲区

目录 前言: 一、环形 FIFO 核心本质与应用场景 1. 什么是环形缓冲区 2. 解决的核心痛点 3. 典型工业级落地场景 二、核心实现原理 1. 双指针循环机制 2. 零拷贝读写 3. 字节流式存储 三、工业级设计规范 1. 封装性设计 2. 接口设计原则 3. 鲁棒性要求 …

2026/7/21 15:49:37阅读更多 →
变频器的技术应用:接线与基本参数设置

变频器的技术应用:接线与基本参数设置

部分参考自《变频器与步进/伺服驱动技术完全精通教程》 化学工业出版社 图源:部分引自《台达 VFD-M变频器 使用手册》,其余引自互联网。 学习笔记,记录一下新变频器到手后,需要进行的配置工作。 说起来,变频器属于传动…

2026/7/21 15:49:37阅读更多 →
Data-Science-EBooks:数据科学学习者的终极免费资源宝库

Data-Science-EBooks:数据科学学习者的终极免费资源宝库

Data-Science-EBooks:数据科学学习者的终极免费资源宝库 【免费下载链接】Data-Science-EBooks Data Science E-books, Interview Resources and Cheat-sheets 项目地址: https://gitcode.com/gh_mirrors/da/Data-Science-EBooks 你是否正在寻找高质量的数据…

2026/7/21 15:47:34阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 0:51:49阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 0:51:49阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 0:51:49阅读更多 →
Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

📌教程适配:OpenClaw v2.7.9 | 兼容 Windows10/11、macOS 双系统 📖前言 当下各类本地 AI 工具层出不穷,多数产品仅能完成文字问答交互,很难直接操控电脑执行实际操作。OpenClaw,业内常称小龙虾 AI&#…

2026/7/21 0:01:46阅读更多 →
Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

聊《一次Codex项目复盘,问题最后出在流程而不是模型》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&…

2026/7/21 0:01:46阅读更多 →
手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

大家好,还是我。前几期带大家做了心情日记本和可视化大屏,后台有朋友留言:“能不能教点好玩的?我想做游戏,但一行代码都不会。”行,这期就安排。今天的目标:从零做一个五子棋游戏。 带AI对战、三…

2026/7/21 0:03:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/20 22:51:39阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/20 18:51:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/20 18:51:18阅读更多 →