[Java EE] 多线程 -- 初阶(1) - 详解

news/2025/12/9 20:07:27/文章来源:https://www.cnblogs.com/ljbguanli/p/19328239

[Java EE] 多线程 -- 初阶(1) - 详解

1.线程(Thread)

1.1 线程是什么?

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

1.2 线程的作用

并发式编程

单核 CPU 的发展遇到了瓶颈 , 要想提高算力 , 就需要多核 CPU ; 而并发式编程能更充分的利用多核 CPU 资源

有些任务场景需要 "等待IO" , 为了让等待 IO 的时间能够取做一些其他工作 , 也需要用到并发编程

② 虽然多进程也能实现 并发编程 , 但是 线程比进程更轻量

  • 创建线程比创建进程更快
  • 销毁线程比销毁进程更快
  • 调度线程比调度进程更快

③ 线程虽然比进程轻量 , 但是还引入了 "线程池"(ThreadPool) 和 "协程"(Coroutine)

1.3 线程和进程区别

① 进程是包含线程的

  • 每个进程至少包含一个线程 , 即为主线程

② 进程和进程之间不共享内存空间 ; 同一个进程的线程之间共享同一个内存空间

③ 进程是系统分配资源的最小单位 ; 线程是系统调度的最小单位

④ 一个进程挂了一般不会影响其他进程 ; 但是一个线程挂了 , 可能把同进程内的其他线程一起带走(整个线程崩溃)

1.4 Java 线程和操作系统线程的关系

线程是操作系统的概念 ; 操作系统内核实现了线程这样的机制 , 并且对用户层提供了一些 API 供用户使用

Java 标准库中的 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装

2.创建线程

方法 1 : 继承 Thread 类

class Mythread extends Thread{//run 相当于进程的入口@Overridepublic void run() {while(true){System.out.println("hello thread");//调用这个方法会抛异常,由于这个类是继承与Thread的,run方法是继承与Thread中的run//子类重写方法抛出的异常,必须是父类方法异常的子类或相同类型,且不能抛出更宽泛的异常try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new Mythread();//父类引用指向子类实例t.start();//启动线程//t.run();//只是调用这个方法,并不是创建进程while (true) {System.out.println("hello main");Thread.sleep(1000);//向上抛异常}}
}

继承 Thread 类, 直接使⽤ this 就表⽰当前线程对象的引⽤.

使用 jconsole 命令来观察线程

Java\jdk\bin 目录下(安装 jdk 的路径) 找 jconsole.exe , 并连接

Thread.sleep(1000) 会让当前进程放弃 CPU 资源 , 进入休眠状态 (毫秒), 此时 CPU 可以调度另一个线程执行

方法 2 : 实现 Runnable 接口

class MyRunnable implements Runnable{@Overridepublic void run() {while(true){System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class demo2 {public static void main(String[] args) throws InterruptedException {//创建 Thread 类实例 , 调用 Thread 的构造方法时 , 将 Runna 对象作为 target 参数Runnable myRunnable = new MyRunnable();//父类接口的引用变量指向子类实例Thread t = new Thread(myRunnable);t.start();//还是需要用到 t (Thread) 来开启线程while (true){System.out.println("hello main");Thread.sleep(1000);}}
}

实现 Runnable 接口 , this 表示的是 MyRunnable 的引用 , 需要使用 Thread.currentThread()

方法 3 : 匿名内部类创建 Thread 子类对象

public class demo3 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();while (true){System.out.println("hello main");Thread.sleep(1000);}}
}

new Thread(){...}创建了 Thread 类的匿名内部类 , 并重写 run() 方法

方法 4 : 匿名内部类创建 Runnable 子类对象

public class demo4 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();while(true){System.out.println("hello main");Thread.sleep(1000);}}
}

方法 5 : lambda 表达式创建 Runnable 子类对象

public class demo5 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();while(true){System.out.println("hello main");Thread.sleep(1000);}}

3.Thread 类

3.1Thread 的常见构造方法

方法

说明

Thread()

创建线程对象

Thread(Runnable target)

使用 Runnable 对象创建线程对象

Thread(String name)

创建线程对象 , 并命名

Thread(Runnable target , String name)

使用 Runnable 对象创建线程对象 , 并命名

[ 了解 ]Thread(ThreadGroup group , Runnable target)

线程可以被用来分组管理 , 分好的组即为线程

ThreadGroup : 线程组 , 把多个线程放到一个组里 , 统一针对线程里所有的线程进行一些属性设置

class Mythread1 extends Thread{public Mythread1(String name) {super(name);}//run 相当于进程的入口@Overridepublic void run() {while(true){System.out.println("hello thread");//调用这个方法会抛异常,由于这个类是继承与Thread的,run方法是继承与Thread中的run//子类重写方法抛出的异常,必须是父类方法异常的子类或相同类型,且不能抛出更宽泛的异常try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class demo6 {public static void main(String[] args) {Thread t1 = new Thread(()->{while(true){System.out.println("hello t1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"这是线程一");//线程命名为th1t1.start();Mythread1 t2 = new Mythread1("这是线程二");t2.start();}
}

3.2 Thread 的常见属性

属性

获取方法

ID

getId()

名称

getName()

状态

getState()

优先级

getPriority()

是否后台线程

isDaemon()

是否存活

isAlive()

是否被中断

isInterrupted()

  • ID : 线程的唯一标识符 , 不同线程不会重复
  • 名称 : 是用于调试工具(jstack)区分线程 , 可以通过构造方法或者 setName()自定义
  • 状态 : 表示线程当前所处的状态 (新建 , 可运行 , 阻塞 , 无限期等待 , 超时等待 , 终止)

状态

含义及场景

NEW(新建)

线程已创建(如new Thread()),但未调用start()

方法,未与操作系统线程关联。

RUNNABLE(可运行)

线程调用start()后进入此状态,包含 “就绪”(等待 CPU 调度)和 “运行中”(正在执行run()方法)两种子状态。

BLOCKED(阻塞)

线程因竞争synchronized锁失败而等待锁,例如多个线程争抢同一把对象锁时,未抢到的线程进入此状态。

WAITING(无限期等待)

线程无超时地等待被其他线程唤醒,如调用Object.wait()(无超时)、Thread.join()(无超时)、LockSupport.park()等方法后进入此状态。

TIMED_WAITING(超时等待)

线程在指定时间内等待,超时后自动唤醒,如调用Thread.sleep(time)Object.wait(time)Thread.join(time)等方法后进入此状态。

TERMINATED(终止)

线程的run()方法执行完毕(正常结束或因异常终止),生命周期结束,无法再被启动。

  • 优先级 : 范围 1-10(默认 5 ) , 优先级高的线程理论上更容易被调度到
  • 是否后台线程 : 后台线程(守护线程) 随 JVM 中所有非后台线程结束而终止 , 典型如垃圾回收线程
  • 是否存活 : 判断 run()方法是否执行完毕 , start()后到 run()结束前返回 true
  • 是否被中断 : 检测线程中断状态( 不会清楚中断标记) , 需要结合 interrupt()方法理解中断机制

部分示例 :

isAlive();
public class demo7 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0; i < 3; i++) {System.out.println("hello t");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});System.out.println(t.isAlive());//还没有start(),一定是falset.start();while(true){System.out.println(t.isAlive());//在run()结束之前,一定是trueThread.sleep(1000);}}
}

设置后台线程 setDaemon();
public class demo8 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.setDaemon(true);//设置t为后台线程,会随着非后台线程的结束而终止,也就是说main线程结束t线程也结束//如果不加该语句,则main线程结束t线程继续执行,互不干扰t.start();for (int i = 0; i < 3; i++) {System.out.println("hello main");Thread.sleep(1000);}System.out.println("main 线程结束");}
}

自己编写的线程一般默认为前台线程

3.3 启动线程 -- start()

run()是线程的入口 , 不需要手动调用 , start()是调用系统 api

调用 start()方法 , 才真的是在操作系统的底层创建了一个线程

public class demo11 {public static void main(String[] args) {Thread t = new Thread(()->System.out.println("线程1"));//t.start();//t.run();t.start();}
}

每个 Thread 对象 , 只能 start 一次 , 否则抛异常

创建 t 线程的逻辑在 main 中 , 因此一定是先执行 main 线程

3.4 中断线程

让线程的入口方法 , 尽快结束

方法 1 : 引入一个isFinished 变量(引入自定义的变量来作为标志位)

public class Demo10 {private static boolean isFinished = false;public static void main(String[] args) throws InterruptedException {//boolean isFinished = false;//如果将变量放在这里,会触发lambda变量捕获//此时如果main线程执行完了,对应的isFinished就销毁了//从而改成内部类访问外部类成员Thread t = new Thread(() -> {//内部类访问外部类成员while (!isFinished) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("thread 结束");});t.start();Thread.sleep(3000);isFinished = true;}
}

方法 2 : 方法 2 : 使⽤ Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替⾃定义标志位

Thread.currentThread() : 用来返回线程的的名称

方法

说明

public void interrupt()

中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标识位

public static boolean interrupted()

判断当前线程的中断标志位是否设置,调用后清楚标志位

public boolean isInterrupted()

判断对象关联的线程的标志位是否设置,标志后不清楚标志位

public class demo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(!Thread.currentThread().isInterrupted()){//判定线程是否被终止了System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {//throw new RuntimeException(e)break;}}System.out.println("t线程结束");});t.start();Thread.sleep(3000);System.out.println("main 线程尝试终止 t 线程");t.interrupt();//主动去终止线程,由于线程中大部分时间都在休眠,此时还会唤醒sleep这样的阻塞方法,捕获到异常从而终止,针对异常的处理,使用break结束循环//会改变isInterrupted()的值为true}
}

使用 t.interrupted()方法通知线程结束

thread 收到通知的⽅式有两种:

  1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通
    知,清除中断标志
    当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽
    略这个异常, 也可以跳出循环结束线程.
  2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过
    Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志 
    这种⽅式通知收到的更及时,即使线程正在 sleep 也可以⻢上收到
异常处理

如果异常不做处理

此处认为这个线程可以是立即结束(break),等会结束(在 catch 中的 break 前编写需要的代码),还是不结束(在 catch 中不写 break 也就是忽略这个终止信号)

3.5 等待一个线程 -- join()

方法

说明

public void join()

等待线程结束

public void join(long millis)

等待线程结束,最多等 millis 毫秒

public void join(long millis,int nanos)

同理,但可以更高精度

public class demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("thread 线程结束");});System.out.println("hello main");t.start();System.out.println("main 线程");t.join(3000);//main线程最多等3秒System.out.println("main 线程结束");}
}

在 main 线程中调用 , 意味着让 main 线程等待 t 线程执行完毕在接着执行 main

3.6 获取当前线程的引用(前面使用过)

方法

说明

public static Thread currentThread();

返回当前线程对象的引用

哪个线程调用这个方法 , 返回哪个线程的引用

3.7 休眠当前线程(前面使用过)

方法

说明

public static void sleep(long millis)throws InterruptedException

休眠当前线程 millis 毫秒

public static void sleep(long millis,int nanos)throws InterruptedException

同理,获取更高精度

由于线程的调度不可控 , 所以这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间

sleep(0)特殊写法,让当前线程放弃 CPU 资源 , 等待操作系统重新调度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/995479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AQS与CAS深度讲解

下面我给你 一口气把 AQS 与 CAS 讲透。你有十年 Java 经验,我直接按“面试+底层原理+源码模型”这种工程师能秒懂的方式来讲,不整花里胡哨的。🔥 一、CAS:并发世界的基石(Compare-And-Swap) 1. CAS 是什么? 一…

PKU 数据结构与算法 2025 复习题 坐公交

题目大意 A 城市有 n 个路口,由 n-1 条道路连接,保证所有路口可以通过这些道路相互联通。 A 城市总共有 m 条公交线路,第 i 条公交在路口 x[i] , y[i] 之间双向往复开行。经过并停靠路口 x[i] , y[i] 最短路径上每一…

2025 雅思培训班怎么选?5 大热门机构深度测评 + 避坑指南

2025 雅思培训班怎么选?5 大热门机构深度测评 + 避坑指南作为一名深耕留学备考领域的教育博主,最近后台被问爆了 “雅思培训班该怎么选”。毕竟市面上机构鱼龙混杂,“保分承诺”“名师授课” 的噱头满天飞,稍不注意…

2025年12月模内注塑技术标杆厂商最新推荐:腾达鑫电子科技,引领IML/IMD/IMR/IMP个性化新标准

随着消费电子、家电、汽车等产业对产品外观、耐用性与环保性要求不断提高,模内注塑技术已从传统制造业逐步渗透至智能终端、高端装饰、功能性面板等多个领域。2025年,模内注塑市场规模预计持续增长,但市场上厂商技术…

2025年12月广东佛山智能电动伸缩门厂家TOP推荐:圣田智能科技,安全智能双标杆

随着智慧城市建设加速推进,智能出入口管理需求持续攀升,电动伸缩门作为机关单位、工业园区、住宅小区等场景的核心安防设备,市场规模预计 2025 年将突破 210 亿元。行业快速发展的同时,也出现厂商技术水平、产品质…

ISCTF misc+web部分wp

Web b@by n0t1ce b0ard 可以通过搜索发现作者复现的文章 作者文章: 可以直接利用POC POST /registration.php HTTP/1.1 Host: 127.0.0.1:8081 Content-Length: 1172 Cache-Control: max-age=0 sec-ch-ua: "Chromi…

CF1046I Say Hello - crazy-

二分/三分,模拟 题意 平面上有两个人。有 \(n\) 个时刻,对于每个人,已知他在每个时刻的位置,且他们总会在两个位置间匀速移动。 如果他们的距离小于等于 \(d _ 1\),并且这是他们第一次交谈或者在他们上次互相打招…

church函数与区间算术

上一篇随笔漏了练习2.5和2.6,这边补充一下。 练习2.5要求证明如果能够将a和b的序对表示为乘积(2a)*(3b)对应的整数,我们可以只用非负整数和算术运算表示序对。 这个表述有点绕口,我最初理解为用(2a)*(3b)表示任意整…

day31-GraphRAG

GraphRAG快速入门与原理详解 一、GraphRAG快速入门介绍 当前阶段大模型的应用落地急需解决的核心问题有一个是:如何与私域数据交互。而私域数据主要的问题是:需要有效地将企业数据整合进大语言模型中,但由于大模型的…

Python 函数与 lambda 表达式的结合

示例代码 def calculate(data, func): """高阶函数:对数据列表中的每个元素应用指定函数""" result = [] for item in data: result.append(func(item)) return result numbers = [1, …

最短路径 - Dijkstra(堆优化)中优先队列的懒删除如何理解?

什么是懒删除? 在Dijkstra算法中,同一个节点可能被多次加入优先队列,但只有最短的那次才是有效的。懒删除就是"推迟删除",直到真正从队列中取出时再判断是否有效。 举个例子理解 假设有这样一个图: A -…

最短路径 - Dijkstra(堆优化)中优先队列的懒删除如何理解?

什么是懒删除? 在Dijkstra算法中,同一个节点可能被多次加入优先队列,但只有最短的那次才是有效的。懒删除就是"推迟删除",直到真正从队列中取出时再判断是否有效。 举个例子理解 假设有这样一个图: A -…

中小企业走向境外资本市场:境外上市辅导、美股上市实践与中国境外券商投行机构角色——以顺安资本为例

一、为什么境外上市辅导机构突然变得这么“重要”? 过去几年,一个现象在资本市场悄悄变得常见:很多原本只考虑新三板、北交所甚至区域股权市场的中小企业,开始把目光直接投向美股、港股等境外市场,关键词搜索里,…

解码生命蓝图,预见健康未来:北京守嘉健康基因检测业务介绍

在精准医疗时代,基因是决定个体健康、特质与潜能的核心密码。守嘉(北京)健康管理服务有限公司,作为中国文化信息协会中医药文化工作委员会的战略共建单位,以前沿的科技视野与专业的服务体系,将基因检测从科研殿堂…

2025年12月注浆工程厂家推荐:安徽林固,道路注浆、空鼓注浆、公路注浆、路基注浆、地基注浆、厂房注浆、地坪注浆、矿山注浆、多场景注浆解决方案服务商

随着基础设施建设提质升级、工程质量标准趋严及施工效率提升需求增加,专业注浆服务已从传统建筑领域逐步拓展至公路、厂房、矿山等多个场景,2025 年市场需求预计持续增长。但市场扩张也带来服务团队专业度、施工适配…

day30-AgentRag应用开发

Agentic RAG 架构 Agentic RAG 架构的基本原理 在大模型领域技术的发展历程中,提高信息检索的效率和准确性是各行各业都在急需解决的应用难题。在前几年技术地位的是检索增强生成 (RAG)技术,通过将大模型 ( LLMs ) 与…

116.Java深入学习之JVM二

116.Java深入学习之JVM二今天整理的内容为 直接内存和垃圾回收 直接内存正常来说需要一步一步才能访问到jvm的堆内存 但是直接内存: 过 java.nio(java I/O库)直接缓冲区直接分配 绕过堆,由操作系统直接管理的本地内存…

第五十八篇

今天是12月9号,上了英语

洛谷 P1203 [USACO1.1] 坏掉的项链 Broken Necklace 题解 最短代码|详细

事情是这样的: 今天我在洛谷上刷题,遇到一个UASCO的题,虽然是橙题,但是还是很有滋味的 看了看大佬的思路发现太抽象了,评论区不是%%%就是orz,因此有了这篇题解题解原文 这绝对是最短的题解了。。。(难理解别打我…

2025年12月佛山二手房拍卖机构标杆推荐:佛山房屋拍卖推荐佛山市中正易拍拍卖有限公司

随着司法拍卖机制日益规范、信息透明度持续提升,以及购房者对高性价比房产的关注度不断上升,法拍房市场在2025年迎来新一轮发展契机。尤其在佛山等珠三角核心城市,法拍住宅因价格优势、房源多样等特点,逐渐成为刚需…