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

【Java并发基础】多线程核心知识详解(线程及创建、生命周期、线程中断机制,线程安全问题)

本文整理了多线程的核心知识,从线程概念、创建方式、生命周期、常用方法,到线程安全与JMM内存模型。

一、多线程基础

①进程(Process):是操作系统分配资源的基本单位,比如打开一个浏览器,就是一个进程。---高隔离性,互不影响


每个进程都有:
·独立的内存空间
·独立的资源(文件、句柄等)

不同进程:

·不能直接访问彼此内存

·需要通过 IPC(进程间通信:如管道,消息队列,Socket... ...)

② 线程(Thread):是程序执行的最小单位,一个进程中可以有多个线程,这些线程共享进程的资源。---高性能,并发,任务间共享数据

线程之间共享:内存(堆)+全局变量+文件资源

但每个线程也有自己的:栈空间+程序计数器

单线程进程:只有一个执行流
多线程进程:多个线程同时执行任务(浏览器进程里可能有:一个线程负责UI,一个线程负责网络请求... ...)

进程vs线程
对比项进程线程
定义资源分配单位执行单位
内存独立共享所属的进程
创建开销
切换开销
通信方式IPC(复杂)直接共享(简单但危险)
崩溃影响不影响其他进程可能导致整个进程崩溃

Q:为什么需要多线程?

① 提高 CPU 利用率

单线程无法充分利用多核 CPU,多线程可以并发执行。

并发(Concurrency):多任务“轮流执行”,单核 CPU 也可以实现,任务A → 任务B → 任务A → 任务B → …(本质:快速切换)
并行(Parallelism):多任务“同时执行”,需要多核 CPU,核心1:任务A,核心2:任务B(本质:真正同时执行)

② 提高程序效率(并发)
IO等待时可以执行其他任务
减少整体执行时间
③ 线程比进程更轻量
创建更快
切换更快
调度开销更小

二、线程的创建方法

1. 继承 Thread
class MyThread extends Thread { @Override public void run() { System.out.println("线程执行"); } } // 启动 new MyThread().start();
2. 实现 Runnable(推荐):避免继承限制,更灵活

使用 Runnable 可以避免 Java 单继承的限制,单继承就是class A extends B, C {} // 不允许

class MyThread extends Thread这个类,已经继承了Thread,就不能再继承别的类了

class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行"); } } Thread t = new Thread(new MyRunnable()); t.start();
3. Lambda 写法(常用):可以简化代码

Lambda 是一种“简化匿名函数”的写法,把“方法”当成参数传。

Lambda 的基本语法:(参数) -> { 方法体 }

new Thread(() -> { System.out.println("线程执行"); }).start();

三、Thread 常用方法

start():t.start();启动线程(真正创建线程)

sleep():Thread.sleep(1000);让线程休眠1000ms(不会释放锁)

join():t.join()所在的主线程等待t线程执行完

interrupt():t.interrupt()中断线程(协作机制,不是强制停止)

currentThread():Thread.currentThread();获取当前线程

四、线程的生命周期(重点)

线程状态(Thread.State):

  • NEW:新建
  • RUNNABLE:可运行
  • BLOCKED:阻塞(锁竞争)
  • WAITING:等待
  • TIMED_WAITING:超时等待
  • TERMINATED:结束
public class ThreadStateDemo { private static final Object lock = new Object(); public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { try { // RUNNABLE synchronized (lock) { System.out.println("t1 获取到锁"); // TIMED_WAITING(sleep) Thread.sleep(1000); // WAITING(wait) System.out.println("t1 进入等待"); lock.wait(); // 被唤醒 → RUNNABLE System.out.println("t1 被唤醒,继续执行"); } } catch (InterruptedException e) { e.printStackTrace(); } }); // NEW 状态 System.out.println("t1 state: " + t1.getState()); // 启动线程 → RUNNABLE t1.start(); Thread.sleep(100); // 确保 t1 先拿到锁 Thread t2 = new Thread(() -> { synchronized (lock) { System.out.println("t2 获取到锁"); // 唤醒 WAITING 的线程 lock.notify(); // TIMED_WAITING(sleep) try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); // t2 启动 t2.start(); // main 线程等待 t1 执行完 → WAITING(join) t1.join(); // TERMINATED System.out.println("t1 执行结束,状态:" + t1.getState()); } }

状态转换涉及的方法调用关系

方法用法调用者作用对象是否释放锁状态
sleep让当前线程暂停一段时间,模拟耗时Thread静态方法线程类不释放TIMED_WAITING
wait让当前线程释放锁obj并等待其他线程的通知Object obj对象锁释放WAITING
notify随机唤醒一个在锁对象obj上等待的线程Object obj对象锁---唤醒线程
join让主线程等待当前线程th执行完毕Thread th线程对象(间接)WAITING
yield让当前线程主动让出 CPU,让其他线程有机会执行(不保证生效)Thread静态方法当前线程不释放RUNNABLE

五、线程中断机制

[中断标志位 = true] ≠ [线程已经停止]

它只是一个“请求线程停止”的信号,因为强制杀死线程是不安全的

可能会导致:

  • 数据不一致
  • 锁没释放
  • 资源泄漏

//interrupt(),给线程发中断信号,设置中断标志位为true。线程是否停止取决自己是否处理这个信号 //线程在正常运行--调用 interrupt 后不会立刻停,只是标志位变为 true //线程正在sleep--如果被 interrupt,会提前结束休眠 //线程在阻塞状态Thread.sleep()、wait()、join()--调用 interrupt 后,会立刻抛异常,中断标志位会被清除(变回 false),是否停止,取决于怎么处理异常 thread.interrupt(); //isInterrupted()获取当前线程的中断状态(true / false),不清除只是“查看” Thread.currentThread().isInterrupted(); //interrupted()获取当前线程的中断状态,会清除中断标志(把中断状态从 true 改回 false) Thread.interrupted();

六、线程安全问题(核心重点)

线程安全:多线程执行结果仍然正确
为什么会不安全:

共享数据:多个线程访问同一变量

执行顺序不可控:线程调度是随机的

操作不是原子性 :count++实际上是三步:读取+修改+写回,多线程下可能出错

//共享数据问题(多个线程访问同一变量) class SharedDataDemo { static int count = 0; public static void main(String[] args) { Runnable task = () -> { for (int i = 0; i < 1000; i++) { count++; // 多线程共享变量 } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); } } /* count 被多个线程共享, 两个线程同时修改它 预期:2000,实际:可能小于 2000 因为线程“同时写”,数据被覆盖 */ //执行顺序不可控(线程调度随机) public class OrderDemo { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println("线程1"); }); Thread t2 = new Thread(() -> { System.out.println("线程2"); }); t1.start(); t2.start(); } } /* 可能是:线程1线程2 也可能是:线程2线程1 */ //非原子操作(count++问题) class AtomicDemo { static int count = 0; public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { count++; }); Thread t2 = new Thread(() -> { count++; }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count = " + count); } } /* 多个线程同时读取同一初始值并各自修改后写回,导致后一次写覆盖前一次结果,从而出现数据丢失。 */

Java 内存模型:主内存 → 线程工作内存 → 修改 → 写回主内存

问题来源:
① 可见性问题:一个线程修改,另一个线程看不到

② 原子性问题:操作被拆分

③ 有序性问题:指令顺序可能改变

指令重排序:编译器 / CPU 会调整代码执行顺序,提高性能

int a = 0;
int b = 0;

a = 1;
b = a;

→实际执行顺序可能变成:

b = a;
a = 1;

http://www.jsqmd.com/news/533553/

相关文章:

  • Llama-3.2V-11B-cot应用实践:跨境电商商品图合规性自动审核
  • 智慧城管的范式革命:从“数字城管”到“城市大脑”的智能生态跃迁(PPT)
  • Ubuntu 20.04下移远RM500Q模块拨号上网全流程(含内核编译避坑指南)
  • python协同过滤算法基于的“健康食谱”食材搭配管理系统vue3
  • Kaggle竞赛老手不会告诉你的秘密:用Albumentations做图像增强,防止CNN过拟合的5个技巧
  • 卡证检测矫正模型行业解决方案:公安户籍系统证件图像预处理模块
  • HTML-
  • MCP本地数据库连接器Connection Refused异常深度溯源(含Wireshark抓包比对+systemd服务依赖图谱)
  • LeetCode经典算法面试题 #295:数据流的中位数(双堆法、有序列表、平衡树等多种实现方案详解)
  • PyTorch 2.8镜像保姆级教程:RTX 4090D用户配置Git/vim/htop等开发工具链
  • FPGA新手必看:Vivado 2018.3从Verilog代码到比特流下载全流程避坑指南
  • Java后端转AI应用开发:3个月就能上手的实战路线
  • 嵌入式 Telegram Bot 客户端:ESP32/Arduino 轻量级非阻塞实现
  • 2026年旋转阀采购避坑:化工行业选型核心指标
  • 3个步骤掌握AI驱动的图像矢量化:零基础玩转位图转矢量图工具
  • 实战指南:基于快马ai为ubuntu24.04生成生产级web应用集群部署代码
  • 科哥定制版FunASR:内置语言模型,显著提升识别准确率
  • 保姆级教程:给若依(RuoYi)前后端分离项目加上Base64接口加密(附完整代码)
  • 讲讲汤阴新兴工程塑化实力怎么样,产品价格贵不贵 - myqiye
  • 算法/力扣--链表经典题目
  • 开箱即用:Ollama平台Phi-3-mini镜像,一键开启AI对话功能
  • 2026上海高端腕表鉴定费用全解析:36大品牌收费标准+六城正规门店指南 - 时光修表匠
  • 计算机毕业设计:美食推荐系统设计与协同过滤算法应用 Django框架 可视化 协同过滤推荐算法 菜谱 食品 机器学习(建议收藏)✅
  • 2026年北京口碑好的工部优选十大品牌推荐,专业评选规则全解析 - 工业品牌热点
  • 图像矢量化:从位图到矢量图的智能转换技术全解析
  • FreeCAD参数化设计实战:3步打造你的智能机械零件库
  • 3个让你彻底告别手动操作的英雄联盟智能助手方案
  • 细聊2026年工业用不锈钢管制造厂,选购时如何选到好用的厂家 - mypinpai
  • 【深度解析】立式注塑机多少钱一台?核心技术与应用:从原理到价值落地 - 速递信息
  • 基于JMeter与STOMP协议,构建高并发WebSocket消息推送压测方案