Java 多线程进阶:线程安全、synchronized、死锁、wait/notify 全解析(含代码示例)

在 Java 并发编程中,“线程安全” 是核心议题之一。本文将深入讲解线程安全的实现手段、synchronized 的使用方式、可重入锁、死锁的成因与避免、wait/notify 通信机制等,并配合实际代码案例,帮助你彻底搞懂 Java 线程协作机制。


一、线程安全与加锁机制

1. synchronized 的使用方式

synchronized 是 Java 最基本的加锁工具,保证代码块在多个线程中“互斥”执行。

① 修饰普通方法(锁的是当前实例 this
public synchronized void syncMethod() {// 线程安全的逻辑
}

② 修饰静态方法(锁的是当前类的 .class 对象)

public synchronized static void staticSyncMethod() {// 静态同步逻辑
}

③ 修饰代码块(可以灵活选择锁对象)

public void method() {synchronized (this) {// 同步逻辑}
}

2. 锁竞争与锁冲突

  • 同一对象加锁:多个线程竞争同一把锁,会造成阻塞等待(锁冲突)。

  • 不同对象加锁:互不干扰,线程可并发执行。

Runnable task = () -> {synchronized (lockObject) {// 临界区代码}
};

二、可重入性:不会死锁的“重复加锁”

Java 的 synchronized可重入锁。也就是说,一个线程可以多次获得同一把锁,不会导致死锁。

public synchronized void outer() {inner(); // 同一线程再次进入 synchronized 方法
}public synchronized void inner() {// 安全执行
}

三、死锁问题与避免

死锁产生的典型场景

1. 两个线程两把锁,互相等待对方释放
class DeadlockExample {private final Object lock1 = new Object();private final Object lock2 = new Object();public void task1() {synchronized (lock1) {System.out.println("Task1 获得了lock1");try { Thread.sleep(100); } catch (InterruptedException ignored) {}synchronized (lock2) {System.out.println("Task1 获得了lock2");}}}public void task2() {synchronized (lock2) {System.out.println("Task2 获得了lock2");try { Thread.sleep(100); } catch (InterruptedException ignored) {}synchronized (lock1) {System.out.println("Task2 获得了lock1");}}}
}

 这个例子满足死锁的 4 个必要条件,其中最核心的是“循环等待”。

死锁避免策略

  • 统一加锁顺序:总是先加 lock1,再加 lock2,避免循环依赖。

  • 使用 tryLock() + 超时机制(需使用 ReentrantLock)。


四、线程通信:wait 和 notify 的正确使用方式

使用wait()notify() 方法

  • wait():线程 自愿等待,进入“暂停”状态,直到被别人叫醒。

  • notify()叫醒一个正在等待的线程。

  • notifyAll():叫醒所有等待的线程(但只有一个能拿到锁继续执行)。

使用场景举例:先执行线程 t1 的一部分,再由线程 t2 接力。

class Task {private final Object lock = new Object();private boolean ready = false;public void part1() {synchronized (lock) {System.out.println("T1 正在执行前半部分任务");try { Thread.sleep(1000); } catch (InterruptedException ignored) {}ready = true;lock.notify(); // 唤醒 T2}}public void part2() {synchronized (lock) {while (!ready) {try {lock.wait(); // 主动释放锁并阻塞} catch (InterruptedException ignored) {}}System.out.println("T2 收到通知,继续执行后续任务");}}
}public class WaitNotifyDemo {public static void main(String[] args) {Task task = new Task();Thread t1 = new Thread(task::part1);Thread t2 = new Thread(task::part2);t2.start(); // T2 先 waitt1.start(); // T1 后 notify}
}

join()sleep() 相比,wait/notify 更灵活,支持提前唤醒和条件控制。


wait 的底层流程

  1. 释放锁

  2. 阻塞等待

  3. 被唤醒后重新竞争锁

  4. 重新获取锁并继续执行


notify 与 notifyAll 的区别

  • notify():随机唤醒一个正在 wait() 的线程。

  • notifyAll():唤醒所有等待线程,但只有一个能成功获得锁。


五、volatile 与内存可见性

在多线程环境中,每个线程可能并不直接操作主内存中的变量,而是从主内存读取变量到自己的缓存中进行操作。这就可能出现这样的情况:

  • 一个线程修改了变量的值,但另一个线程看不到这个变化(因为仍在用旧的缓存)。

  • 导致线程间的通信出现“看不见的修改”。

这就是内存可见性问题

示例代码

public class VisibilityProblem {private static boolean running = true;public static void main(String[] args) {Thread t = new Thread(() -> {while (running) {// 执行代码}System.out.println("线程停止");});t.start();try { Thread.sleep(1000); } catch (InterruptedException ignored) {}running = false; // 主线程修改 runningSystem.out.println("主线程修改 running 为 false");}
}

可能结果:

即使主线程已经把 running 改为 falset 线程可能还一直在死循环,因为它使用的是本地缓存值而不是主内存的值。

解决方式:使用 volatile

private static volatile boolean running = true;

 一旦使用 volatile 修饰变量,修改后的值会立刻刷新到主内存,并且所有线程每次访问变量时都会从主内存读取,从而保证了内存可见性。


结语

Java 多线程的本质是对“共享资源 + 并发访问”下的一种控制与协作。理解 synchronized 的使用方式、死锁的本质、以及 wait/notify 的协作机制,能有效帮助我们写出更安全、灵活的并发程序。

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

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

相关文章

高并发场景下的MySQL生存指南

引言 在2025年全球数字经济峰会上,阿里云披露其核心交易系统单日处理请求量突破万亿次,其中MySQL集群承载了78%的OLTP业务。这标志着数据库系统已进入百万级QPS时代,传统优化手段面临三大挑战: 一、硬件与架构优化:构…

MCP入门

什么是mcp mcp(model context protocol,模型上下文协议) 标准化协议:让大模型用统一的方式来调用工具,是llm和工具之间的桥梁 A2A:Agent-to-Agent协议 mcp通信机制 提供mcp服务查询的平台 具有工具合集…

服务容错治理框架resilience4jsentinel基础应用---微服务的限流/熔断/降级解决方案

继续上一章未完成的sentinel; 直接实操; 关于测试:本文使用线程池线程异步执行模拟并发结合Mock框架测试 其他文章 服务容错治理框架resilience4j&sentinel基础应用---微服务的限流/熔断/降级解决方案-CSDN博客 conda管理python环境-…

深入理解 C 语言中的变量作用域与链接性:`extern`、`static` 与全局变量

深入理解 C 语言中的变量作用域与链接性:extern、static 与全局变量 在 C 语言中,变量的作用域(Scope)和链接性(Linkage)是理解程序结构和模块化的关键概念。本文将详细探讨在函数外定义的变量是否为全局变…

实验三 软件黑盒测试

实验三 软件黑盒测试使用测试界的一个古老例子---三角形问题来进行等价类划分。输入三个整数a、b和c分别作为三角形的三条边,通过程序判断由这三条边构成的三角形类型是等边三角形、等腰三角形、一般三角形或非三角形(不能构成一个三角形)。其中要求输入变量&#x…

小米首个推理大模型开源——Xiaomi MiMo,为推理而战!

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、MiMo的惊人表现:小参数量,大能力二、双轮驱动&#…

《2025全球机器学习技术大会:阿里云讲师张玉明深度剖析通义灵码AI程序员》

4 月 18 日 - 19 日,由 CSDN & Boolan 联合举办的 2025 全球机器学习技术大会(ML-Summit)于上海顺利举行。大会聚焦人工智能与机器学习前沿技术,汇聚了来自科技与人工智能领域的数位顶尖专家以及数千名开发者和研究者&#xf…

MySQL事务隔离级别详解

MySQL事务隔离级别详解 事务隔离级别概述 MySQL支持四种标准的事务隔离级别,它们定义了事务在并发环境下的可见性规则和可能出现的并发问题: READ UNCOMMITTED(读未提交) • 最低隔离级别 • 事务可以读取其他事务未提交的数据&…

计算机视觉(CV)技术的优势和挑战(本片为InsCode)

计算机视觉(CV)技术是一种利用计算机和算法来模拟人类视觉实现图像和视频处理的技术。它在各个领域都有着广泛的应用,具有许多优势和挑战。 优势: 自动化:CV 技术可以自动识别、分类、跟踪和分析图像和视频数据&…

Android JIT编译:adb shell cmd package compile选项

Android JIT编译:adb shell cmd package compile选项 例如: adb shell cmd package compile -m speed -f --full 包名 配置参数指令说明: compile [-r COMPILATION_REASON] [-m COMPILER_FILTER] [-p PRIORITY] [-f] [--primary-dex] …

Android Kotlin 项目集成 Firebase Cloud Messaging (FCM) 全攻略

Firebase Cloud Messaging (FCM) 是 Google 提供的跨平台消息推送解决方案。以下是在 Android Kotlin 项目中集成 FCM 的详细步骤。 一、前期准备 1. 创建 Firebase 项目 访问 Firebase 控制台点击"添加项目",按照向导创建新项目项目创建完成后&#x…

搭建PCDN大节点,服务器该怎么配

搭建P2P大节点时,服务器要怎么配呢?需要综合考虑硬件性能、网络带宽、存储能力、系统架构以及安全性等多个方面,以确保节点能够高效、稳定地运行。 一、硬件配置 CPU:选择高性能的多核处理器,以满足高并发处理需求。核…

(done) 吴恩达版提示词工程 8. 聊天机器人 (聊天格式设计,上下文内容,点餐机器人)

视频:https://www.bilibili.com/video/BV1Z14y1Z7LJ/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 别人的笔记:https://zhuanlan.zhihu.com/p/626966526 8. 聊天机器人(Chatbot) …

AtCoder Beginner Contest 403(题解ABCDEF)

A - Odd Position Sum #1.奇数数位和 #include<iostream> #include<vector> #include<stdio.h> #include<map> #include<string> #include<algorithm> #include<queue> #include<cstring> #include<stack> #include&l…

【Game】Powerful——Abandoned Ruins(9)

文章目录 1、新增古玩2、机关机制3、探索法宝4、智斗强敌5、地图6、参考 2025 年 1 月迎来的新玩法——荒废遗迹 每周四个宝藏铲&#xff08;老玩法&#xff09;或者两个遗迹线索&#xff08;新玩法&#xff09;&#xff0c;3 个宝藏铲也可以换一个遗迹线索&#xff0c;之前没时…

构建网页版IPFS去中心化网盘

前言&#xff1a;我把它命名为无限网盘 Unlimited network disks&#xff08;ULND&#xff09;&#xff0c;可以实现简单的去中心化存储&#xff0c;其实实现起来并不难&#xff0c;还是依靠强大的IPFS&#xff0c;跟着我一步一步做就可以了。 第一步&#xff1a;准备开发环境…

国标GB28181视频平台EasyGBS在物业视频安防管理服务中的应用方案​

一、方案背景​ 在现代物业服务中&#xff0c;高效的安全管理与便捷的服务运营至关重要。随着科技的不断发展&#xff0c;物业行业对智能化、集成化管理系统的需求日益增长。EasyGBS作为一款基于国标GB28181协议的视频监控平台&#xff0c;具备强大的视频管理与集成能力&#…

[Unity]设置自动打包脚本

背景 我们经常会使用自动打包功能 文件名称: AutoBuild.csusing System.IO; using System.Linq; using UnityEditor; using UnityEngine;public class AutoBuilder {[MenuItem("Build/GetCurrentBuildTarget")]public static void GetCurrentBuildTarget(){Debug.L…

正点原子STM32H743单片机实现ADC多通道检测

目标 使用STM32CubeMX工具&#xff0c;配置ADC相关参数&#xff0c;实现在STM32H743单片机上获取ADC多通道电压值。共14个ADC引脚&#xff0c;ADC2有5个&#xff0c;ADC3有9个&#xff0c;全部设置单通道 ADC引脚 PF3PF4PF5PF10PC0PC2PC3PH2PH3PA3PB0PB1PA4PA5PA6 STM32cube…

深度学习基础(四)——计算量(FLOPs)、参数量(Params)、计算速度(FLOPS/TOPS))

一、计算量FLOPs FLOPs&#xff0c;全称为Floating Point Operations, (s为复数缩写&#xff09;&#xff0c;浮点运算数&#xff0c;指模型完成一次前向传播所需的浮点运算次数&#xff0c;可以理解为计算量&#xff08;模型的时间复杂度&#xff09;&#xff0c;用来衡量算法…