并发内功-线程中断机制全解
前言在 Java 并发编程中,“线程中断”(Thread Interruption)是一个高频出现但常被误解的概念。很多开发者误以为调用 interrupt() 就是“杀死”线程,实际上,Java 的中断机制是一种协作式的协议,而非抢占式的命令。 本文将从底层原理、API 辨析、状态交互以及 JUC 框架应用四个维度,带你彻底搞懂 Java 线程中断。 一、 中断的本质:从“命令”到“协商”1. 为什么废弃 Thread.stop()?Java 早期版本曾提供 Thread.stop() 方法来强制终止线程。这就像 Linux 的 kill -9,它不给线程任何喘息或清理资源的机会,会立即释放线程持有的所有锁。这极易导致数据不一致(例如:转账操作只执行了一半就终止),因此在 JDK 1.2 中即被标记为废弃。 2. 协作式设计Java 选择了**协作式(Cooperative)**中断机制。 动作: 当调用 t.interrupt() 时,JVM 仅仅是将线程 t 内部的一个布尔类型**“中断标志位”置为 true**。 结果: 线程 t...
并发内功-如何确定加什么类型的锁?
前言在 Java 并发编程(JUC)的学习中,我们往往容易陷入 API 的泥潭:掌握了 synchronized 的原理,背熟了 AQS 的源码,但在面对具体的业务场景时,却依然犹豫不决——“这里到底该用哪种锁?为什么?” 在 JDK 1.8 时代,锁的性能差异已不再像早期那么巨大,选型的核心依据从单纯的“性能”转向了**“场景匹配度”与“代码维护性”**。 本文将摒弃枯燥的 API 罗列,直接提供一套从实战出发的并发工具选型决策指南。 🚀 0. 精简速查版 (TL;DR)如果你时间紧迫,请直接参考这份选型决策树。在面对线程安全问题时,按顺序自问: 是数据容器(List, Map, Queue)吗? KV 存储 → ConcurrentHashMap (首选) 需要排序的 KV → ConcurrentSkipListMap 读多写极少(如白名单) → CopyOnWriteArrayList 生产消费队列 → ArrayBlockingQueue (必须有界) 是单个变量的简单操作(如 +1,赋值)吗? 极高并发统计(如接口限流计数) → LongAdder...
并发内功-如何确定加锁粒度
前言 前言 在并发编程中,我们常面临一个两难的抉择: 锁粒度太粗(如直接锁整个方法):虽然绝对安全且代码简单,但系统几乎退化为串行,多核 CPU 成了摆设。 锁粒度太细(如只锁某一行代码):并发度上去了,但带来了复杂的死锁风险,且过频的“加锁/解锁”引发的上下文切换(Context Switch)开销,反而可能拖慢系统。 本文不讨论“如何选择锁对象”或“该用哪种锁类”,只专注解决一个核心问题: 当必须加锁时,我们该如何精准地划定锁的时空范围,从而在保证线程安全的前提下,将性能压榨到极致? ⚡️ 精华速查版 (TL;DR)如果你时间紧迫,请掌握以下关于“粒度控制”的核心心法: 纵向拆分(时间维度): 快进快出。将 I/O、预处理、参数校验、复杂计算移出同步块,只锁住“修改共享数据”的那一瞬间。 横向拆分(空间维度): 专锁专用。如果两个共享变量互不干扰(如用户的“积分”和“头像”),请使用两把独立的锁(锁分离),不要用一把大锁通吃。 化整为零(数据维度): 借鉴 JDK 1.8 ConcurrentHashMap...
并发内功-如何确定加锁对象?
前言在 Java 并发编程中,学会写 synchronized 只是幼儿园水平,学会选择“锁谁”才是迈入高阶开发的门槛。 很多严重的线上 Bug,不是因为没加锁,而是因为锁错了对象。 锁了 this,结果外部代码恶意占用导致死锁。 锁了 Integer,结果自动装箱导致锁对象变了。 锁了 String 常量,结果导致无关的业务模块互相阻塞。 本文将剥离所有复杂的并发理论,单刀直入:当代码存在线程安全问题时,我到底该拿哪个对象当锁? ⚡️ 精华速查版:锁对象决策清单在写代码前,请对照下表进行“灵魂拷问”: 保护目标 ❌ 错误/不推荐写法 ✅ 最佳实践 (锁对象选择) 核心理由 实例变量 (非静态) synchronized(this) private final Object lock = new Object(); 封装性。避免外部代码持有你的对象锁造成干扰。 静态变量 (全局共享) synchronized(this) 或 new...
IDEA快捷键源码阅读神级指南
前言在软件开发中,我们不仅要编写代码,更要花费大量时间阅读代码——无论是团队成员的代码、第三方库的源码,还是经典框架的底层实现。IntelliJ IDEA 作为 Java (及其他语言) 开发的利器,提供了无数强大的功能来帮助我们理解和导航代码。 然而,真正的效率高手从不满足于鼠标点点点。熟练掌握快捷键,能让你在代码的世界里如行云流水般穿梭,将注意力完全集中在逻辑本身。 本文整理了一份在阅读源码时极其高效的 IDEA 快捷键清单,并补充了一些个人压箱底的“神器”,旨在帮助你摆脱鼠标,大幅提升代码阅读和理解的效率。 一、跳转与导航:指哪打哪,精准定位在源码的海洋中,精准、快速地跳转是最高频的操作。 功能名称 快捷键 (Windows/Linux) 用途和说明 查看声明/实现 Ctrl + B 或 Ctrl + 鼠标左键 【最常用】 无需多言的绝对核心。可以从任何变量、方法或类的使用处,一键跳转到其定义的地方。当光标在接口方法上时,它会聪明地弹出所有实现类列表供你选择。 查看实现类 Ctrl + Alt +...
方法参数是值传递还是引用传递?一篇彻底搞懂!
在Java面试和日常开发中,“Java是值传递还是引用传递?”是一个经典问题。很多开发者对此感到困惑,但答案是唯一的:Java永远是值传递(Pass by Value)。 为了彻底厘清这个概念,让我们首先将核心结论放在最前面: 核心结论 当传入的参数是 原始数据类型 (int, double 等) 或 不可变的引用类型 (如 String, Integer) 时: 无论方法内部对这个参数做什么操作,都不会影响到方法外部的原始变量。 当传入的参数是 可变的引用类型 (如 List, Map 等) 时: 如果修改的是对象自身的状态 (例如调用 list.add(item)),这个改变对方法外部是 可见的。 如果将参数引用指向一个新对象 (例如 list = new ArrayList<>()),这个改变对方法外部是 不可见的。 为什么会这样?接下来,我们将通过代码示例和原理解析,深入探讨背后的一切。 什么是值传递 (Pass by Value)?在程序设计语言中,方法调用时传递参数的方式主要有两种:值传递和引用传递。 值传递 (Pass by...
深入解析DCL单例模式:为何volatile不可或缺?
在Java并发编程中,单例模式的实现方式多种多样。其中,懒汉式的“双重检查锁定(Double-Checked Locking, DCL)”因其兼顾了线程安全、懒加载和高性能而备受推崇。然而,其标准实现中的 volatile 关键字常常让初学者感到困惑:既然已经有了 synchronized 锁,为什么还需要 volatile 来修饰实例变量呢?它究竟是画蛇添足,还是点睛之笔? 本文将带你深入剖析DCL的内部工作机制,彻底搞懂 volatile 在其中扮演的至关重要的角色。 完美的DCL实现首先,让我们看一下被广泛推荐的DCL单例模式标准代码(JDK 1.5+): 123456789101112131415161718192021222324252627282930313233343536package com.liboshuai.demo.thread.safe.singleton;/** * 懒汉式单例模式(线程安全 - 双重检查锁定版) * 优点:线程安全,且性能较高,实现了懒加载 * 这是推荐的懒汉式单例模式实现之一 */public class...
Java并发编程中的Volatile什么时候可以使用,什么时候不可以使用?
在Java并发编程中,volatile是一个既强大又容易被误用的关键字。它像一把轻巧的锁,提供了比synchronized更低的开销,但适用场景也更为受限。《Java并发编程实战》一书为我们指明了方向,提出了使用volatile必须同时满足的三个条件。 然而,原书中的表述对于初学者来说可能有些晦涩。本文旨在用最通俗的语言和最直观的代码,带你彻底搞懂volatile的正确用法。 核心回顾:volatile的作用 在深入探讨之前,我们先快速回顾一下volatile的两个核心作用: 可见性(Visibility): 当一个线程修改了volatile变量的值,这个新值对其他线程是立即可见的。它通过防止编译器和CPU的指令重排序、并确保修改后的值立即写回主内存来实现。 有序性(Ordering):...
并发的“幽灵”:为何书中的并发错误在我电脑上无法复现?
你是否遇到过这样的场景:你正在学习《Java并发编程实战》这样的经典书籍,满怀期待地将一个“会出错”的并发示例代码在自己的电脑上运行时,却发现……它每次都能完美运行!书上言之凿凿的无限循环、数据错乱等问题,在你强大的 i7/i9 处理器和最新的IDE面前,消失得无影无踪。 这究竟是书本理论过时了,还是我们的认知出现了偏差? 本文将以《Java并发编程实战》中经典的 NoVisibility 程序清单为例,深入探讨这个现象。这不仅是一个关于代码的问题,更是一次深入理解Java内存模型(JMM)、指令重排序和内存可见性的绝佳机会。 梦开始的地方:经典的 NoVisibility让我们先回顾一下这段经典的代码: 1234567891011121314151617181920// 程序清单3-1 from "Java Concurrency in Practice"public class NoVisibility { private static boolean ready; private static int number; ...
初学Java并发编程,如何看懂“锁重入”?一篇“慢动作”分解带你入门
初学Java并发编程,如何看懂“锁重入”?一篇“慢动作”分解带你入门初学Java并发编程的你,是否也曾对着下面这段经典代码感到困惑? 这段代码出自《Java并发编程实战》一书,它通过一个子类继承父类并调用父类同步方法的例子,引出了“锁重入”的概念。但很多开发者第一次看到时,都会有一个共同的疑问:重入到底发生在哪里了?我怎么没看见? 别担心,这篇博文将用“慢动作”分解的方式,带你彻底看清这个“看不见”却至关重要的概念。 一、令人困惑的经典代码首先,让我们回顾一下这段代码: 12345678910111213// 程序清单 2-7public class Widget { public synchronized void doSomething() { // ... }}public class LoggingWidget extends Widget { public synchronized void doSomething() { ...
