Java线程小记

前言

线程是并发编程的最小执行单元,也是Java高并发体系的基石。无论是锁机制、AQS、线程池、异步任务、中间件底层,全部建立在线程调度的基础之上。

本文基于核心底层原理**,**讲解Java线程核心知识点,覆盖基础原理、面试重难点

一、线程本质:栈结构与操作系统调度模型

1.1 线程的底层结构

线程是操作系统调度CPU的最小单位(进程是资源分配最小单位)。

每一个Java线程,在JVM内部都对应一块独立的虚拟机栈(栈帧)

  • 方法调用 → 栈帧入栈

  • 方法执行结束/返回 → 栈帧出栈

线程之间栈内存相互隔离、互不干扰 ,这也是:局部变量天然线程安全,成员变量/静态变量线程不安全的根本原因。

1.2 多线程诞生的核心意义

CPU 的运算速度远远快于磁盘、网络、IO 读写速度。

单线程最大问题:IO阻塞导致CPU空转浪费

当线程执行IO操作(网络请求、文件读写、数据库查询)时,线程会阻塞、让出CPU,此时CPU无事可做,只能空等。

多线程的核心价值:填补IO空窗期,榨干CPU性能

在线程A IO阻塞期间,CPU可以调度线程B、C执行计算任务,极大提升整体吞吐量。

1.3 多线程一定更快吗?

不一定!多线程只优化「IO密集型任务」

  • IO密集型 :网络、文件、数据库,阻塞时间长 → 多线程大幅提速

  • CPU密集型 :纯计算、循环、算法,几乎无阻塞 → 多线程不提速,甚至变慢

原因:线程创建、销毁、上下文切换需要消耗大量CPU资源。如果任务本身没有大量空闲阻塞,多线程只会徒增切换开销。

终极结论(工程选型准则)

  • 任务阻塞占比高 → 多线程提升效率

  • 任务阻塞占比低(纯计算) → 单线程效率最高

二、线程优先级

2.1 线程优先级本质:代码级别无效

Java提供1~10级线程优先级,但仅为JVM提示,操作系统不保证遵守

真实生产环境:不要依赖线程优先级控制执行顺序,完全不可靠。

2.2 面试高频:如何实现多线程公平竞争CPU?

原生线程启动时机不同、OS随机调度,无法公平竞争。

工程经典方案:notStart 就绪等待 + notEnd 统一终止

  • notStart:所有线程先就绪、阻塞等待,全部就位后统一放开竞争,保证起跑线一致

  • notEnd:统一控制所有线程停止,避免线程执行参差不齐

实现代码:

三、Java线程七大状态

Java线程并非简单的运行/停止,JDK定义七大线程状态,全部由 Thread.State 枚举维护:

  1. NEW 新建:new Thread(),未启动

  2. RUNNABLE 就绪:调用start(),等待CPU调度

  3. RUNNING 运行:被OS选中,正在执行代码

  4. BLOCKED 阻塞:抢锁失败,等待synchronized锁

  5. WAITING 无限等待:wait()、join(),无限等待唤醒

  6. TIMED_WAITING 限时等待:sleep(time)、wait(time)

  7. TERMINATED 死亡:线程执行完毕/异常终止

四、高频面试对比:sleep VS wait

这是并发面试最高频基础题,必须精准区分:

1. 锁行为不同(最核心)

  • sleep :只让出CPU,不释放锁,其他线程无法获取锁

  • wait :让出CPU + 强制释放锁,其他线程可以竞争锁执行

2. 所属类不同

  • sleep:Thread类静态方法

  • wait:Object类方法

3. 使用限制不同

  • wait:必须在同步代码块/锁内执行,否则直接抛异常

  • sleep:任意位置可执行

4. 唤醒机制不同

  • sleep:时间到自动唤醒

  • wait:必须被notify/notifyAll手动唤醒

五、Object类多线程核心方法汇总

Object是所有类父类,其方法支撑了Java最原始的并发协作模型:

1. 并发专属方法

  • wait():释放锁、无限等待

  • wait(time):限时等待

  • notify():随机唤醒一个等待线程

  • notifyAll():唤醒全部等待线程

2. 通用核心方法

  • toString()、equals()、hashCode():对象判等与打印

  • getClass():获取运行时类对象

  • clone():创建对象副本(浅克隆)

六、守护线程 Daemon(后台线程)

6.1 守护线程定义

Java线程分为两类:

  • 前台线程(用户线程):业务主线程,JVM必须等待其执行完毕

  • 守护线程(后台线程) :为前台线程服务,所有前台线程结束,守护线程直接终止

典型案例:GC垃圾回收线程,全程后台运行,程序退出则GC直接退出。

6.2 应用场景

心跳检测、日志异步刷盘、监控统计、后台巡检,不阻塞程序退出的任务。

七、线程中断机制

7.1 核心真相:Java没有真正的强制中断

Java无法强行杀死正在运行的线程

interrupt() 仅仅是 修改线程内部的中断标志位,发一个"中断通知"。

线程是否退出、何时退出,由线程自身代码判断、自主决定

7.2 两种中断模型

1. 原生中断标记中断(interrupt)

线程阻塞状态(sleep/wait)下调用interrupt,会直接抛出中断异常,唤醒线程。

正常运行线程,只会修改标记位,不会停止。

2. 自定义变量中断(工程最常用)

定义全局布尔变量作为开关,外部修改开关,线程内部循环判断,安全退出。

面试结论:线程终止只有两种方式:

  1. run() 方法正常执行完毕

  2. run() 内部抛出未捕获异常

实现代码:

八、经典面试题:使用join实现多线程严格顺序输出

通过链式join,让线程1执行完再执行线程2,线程2执行完再执行线程3,完美实现顺序串行执行。

在下面代码里面的Thread.join()使用逻辑:当前执行代码的线程阻塞等待,直到前置线程执行完毕join方法才返回(让调用线程主动等待目标线程死亡,实现线程执行顺序控制)

实现代码:

九、ThreadLocal

9.1 核心作用

实现线程数据隔离,多线程互不冲突

ThreadLocal 可以理解为:每个线程专属的私有存储空间

9.2 底层原理

每个线程内部维护一个 ThreadLocalMap,以 ThreadLocal 为 key、存储数据为 value。

  • 线程只能读取自己Map中的数据

  • 无法访问其他线程的数据

9.3 弱引用机制(面试必问)

ThreadLocal的key为弱引用 ,目的:防止内存泄漏

当ThreadLocal对象无外部引用时,可被GC回收,避免线程常驻导致的内存溢出。

十、线程池核心原理

10.1 为什么必须用线程池?

频繁创建、销毁线程开销极大(操作系统级资源消耗)。

线程池核心价值:

  • 线程复用,避免频繁创建销毁

  • 统一管理线程、统一分配任务

  • 控制最大并发数,防止系统OOM、CPU打满

  • 缓冲任务,削峰填谷,提升系统稳定性

10.2 线程池核心思想

线程固定、任务排队,解决"任务产生速度 > 线程执行速度"的并发矛盾。

相关推荐
西凉的悲伤1 小时前
Spring Cloud Gateway介绍
java·spring cloud·gateway
摇滚侠1 小时前
01 基础语法 JavaScript 入门到精通全套教程
开发语言·javascript·ecmascript
sleven fung1 小时前
Milvus 向量数据库
开发语言·数据库·python·langchain·milvus
逸Y 仙X1 小时前
文章五:Elasticsearch安全通信
java·大数据·安全·elasticsearch·搜索引擎·全文检索·jenkins
quan26312 小时前
20260529,日常开发-查老数据全量更新闭坑
java·mysql·主从·主从延迟
大大杰哥2 小时前
Java 日志框架详解:SLF4J + Logback 从入门到实战
java·开发语言·logback
ylscode2 小时前
黑客利用 GHOSTYNETWORKS 和 OMEGATECH 托管 JS 恶意软件基础设施
开发语言·安全·php·安全威胁分析
爱吃生蚝的于勒2 小时前
QT开发第二章——信号和槽
c语言·开发语言·c++·qt
xcLeigh2 小时前
Python入门:Python3 operator模块全面学习教程
开发语言·python·学习·教程·python3·operator