禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 计算机组成原理

  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • 浅谈Linux权限管理
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
      • 写在文章开头
      • 详解线程结构设计与常见问题
        • 线程的历史和基本理念
        • 线程基本数据结构分配
        • 线程的基本使用示例
        • 线程的调度
        • 详解线程栈的基本调用
        • 线程栈的内存分配策略
        • 用户态线程和内核态线程的探究
      • 小结
      • 参考
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 浅谈Linux基于信号处理中断的哲学
    • 从操作系统底层浅谈程序栈的高效性
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
    • 来聊聊函数回调
    • 一个完美主义者的自我救赎
  • 计算机网络

  • 运维

  • 编码最佳实践

  • 计算机基础
  • 操作系统
sharkchili
2026-03-25
目录

Linux线程管理

[toc]

# 写在文章开头

本文将从操作系统底层深入探究线程的诞生及其出色的设计理念和调度策略,通过对本文的阅读,你将对计算机程序设计的基本理念和设计思想有更深入的掌握。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)。

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

# 详解线程结构设计与常见问题

# 线程的历史和基本理念

传统操作系统的理念下,进程是一个相对重量级的单位,本质上都是由一个地址空间和一个控制线程构成。对于现代计算机而言,许多的应用活动会随着时间的推移而陷入阻塞。于是就有了基于进程这一单位衍生出一个更加轻量级的准并行运行单位——线程。

通过多个轻量级线程共享进程的地址空间和所有可用的数据,以更加轻量级的方式快速完成多道工序并销毁,创建效率比进程快10~100倍,在保证降低开销的同时提升程序执行效率。 这种设计理念在现代操作系统中会带来质的提升,例如一个支持查找、全局替换、生成分页的字处理软件,在单线程的情况下,针对如下工作都可以通过一条指令一次性对整个文档进行全局管理:

  1. 文本检查
  2. 指定文字替换
  3. 生成页分隔符

对此我们试想一下这样一个场景,我们的字处理软件收到一个300页的未整理过的文件,此时我们希望定位到600页并删除某一行数据,在原有的单线程模型模式下,对应处理步骤则是:

  1. 整理文件,确定分页
  2. 定位到指定数据行
  3. 删除数据,重新分页

这种模式就意味着如果在分页未完成之前,步骤二和步骤三就无法执行。此时我们就可以引入多线程的方案,分别建立:

  1. 一个后台写入刷盘持久化
  2. 自动分页格式化建立分隔符
  3. 读取键盘输入

这3个线程并行处理这些工作,在理想的情况下,我们的修改操作完全可以在格式化线程处理完成之后,快速定位到600页数据完成修改,通过多线程尽可能利用CPU时间片,完成文件处理的各个工序,避免单线程阻塞带来的耗时等待,这就是多线程的设计哲学:

# 线程基本数据结构分配

以同一个进程为例,针对单一进程的职责,开发者可以拆解并创建出多个线程,共享进程的地址空间和内存数据、文件描述符和环境变量信息,并在多核CPU下并行执行,对应的每个线程都具备如下组件:

  1. 程序计数器:记录接下来要执行的指令
  2. 寄存器:保存程序当前的工作变量
  3. 堆栈:记录每个线程的执行历史,例如本示例中的线程通过main调用function1,通过栈帧就可以记录调用历史完成过程调用和返回

由于线程具备进程的部分特质,所以线程常常也被称之为轻量级进程:

# 线程的基本使用示例

关于线程的基本使用,我们从更加接近于底层的C语言展开探究,在Linux系统下,通过C语言内置的库函数就可以完成线程的基本创建和使用,如下代码我们以线程1为例:

  1. 通过pthread_create指明线程执行的函数thread_function_1、通过参数3传值给thread_function_1告知线程id号,并通过返回值确定线程是否成功创建
  2. 通过join使其加入主线程,确保该线程完成执行后才能推出
  3. 通过pthread_exit结束线程的调用
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 线程函数1
void* thread_function_1(void* arg) {
    int thread_id = *(int*)arg;
    
    for (int i = 0; i < 5; i++) {
        printf("线程%d正在执行: %d\n", thread_id, i);
        sleep(1); // 模拟工作
    }
    
    printf("线程%d执行完毕\n", thread_id);
    pthread_exit(NULL);
}

// 线程函数2
void* thread_function_2(void* arg) {
    char* message = (char*)arg;
    
    
    printf("线程2收到消息: %s\n", message);
   
   
    
    printf("线程2执行完毕\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread1, thread2;
    int id1 = 1;
    
    
    // 创建第一个线程
    int result1 = pthread_create(&thread1, NULL, thread_function_1, &id1);
    if (result1 != 0) {
        fprintf(stderr, "创建线程1失败\n");
        exit(EXIT_FAILURE);
    }
    
    // 创建第二个线程
    char* msg = "hello  thread-2";
    int result2 = pthread_create(&thread2, NULL, thread_function_2, msg);
    if (result2 != 0) {
        fprintf(stderr, "创建线程2失败\n");
        exit(EXIT_FAILURE);
    }
    
    // 等待线程1结束
    pthread_join(thread1, NULL);
    printf("主线程: 线程1已结束\n");
    
    // 等待线程2结束
    pthread_join(thread2, NULL);
    printf("主线程: 线程2已结束\n");
    
    printf("所有线程执行完毕,主线程结束\n");
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

对应的输出结果如下,读者可以参考笔者的说明调试理解一下线程的基本使用:

线程1正在执行: 0
线程2收到消息: hello  thread-2
线程2执行完毕
线程1正在执行: 1
线程1正在执行: 2
线程1正在执行: 3
线程1正在执行: 4
线程1执行完毕
主线程: 线程1已结束
主线程: 线程2已结束
所有线程执行完毕,主线程结束
1
2
3
4
5
6
7
8
9
10
11

# 线程的调度

Linux系统操作的哲学是,线程调度也是非常灵活的。若我们的线程执行一个长耗时的任务,希望线程自动让出CPU,就可以通过yield函数让CPU执行其他线程的任务,它仅仅提示操作系统当前线程愿意让出CPU,具体是否采纳这个提示,完全由操作系统决定,所以这也就是为什么笔者的实践中会补充一个sleep阻塞式调用来提升yield的概率:

 Thread t1 = new Thread(() -> {
            Thread.currentThread().setName("t1");
            for (int i = 0; i < 100; i++) {
                if (i == 10) {
                    System.out.println("t1先执行,把cpu时间片让给t2");
                    Thread.yield();
                    //利用sleep阻塞式调用更有效的让出CPU时间片
                    ThreadUtil.sleep(400);

                }
            }
            System.out.println("t1执行结束");
        });

        Thread t2 = new Thread(() -> {
            Thread.currentThread().setName("t2");
            ThreadUtil.sleep(100);
            System.out.println("t2再执行,把cpu时间片让给t1");
            Thread.yield();

        });
        t1.start();
        t2.start();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

若我们希望一个线程可以阻塞等待其他线程完成后结束,则可以通过join函数。但需要注意的是,下面的示例展示的是守护线程的使用方式:


        Thread thread = new Thread(() -> {
            ThreadUtil.sleep(5000);
            System.out.println("子线程执行完成");
        });

        // 将线程设置为守护线程
        thread.setDaemon(true);

        System.out.println("启动子线程...");
        thread.start();

        // 主线程等待2秒后就结束,不等待子线程执行完成
        ThreadUtil.sleep(2000);
        System.out.println("主线程执行结束");
        // 不调用thread.join()方法,主线程直接结束
        // 由于子线程是守护线程,当主线程结束时,JVM会直接退出,不会等待守护线程完成
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 详解线程栈的基本调用

上文提及,线程是作为进程的基本单位,所以同一个进程中的多线程会共享进程的地址空间和数据。为避免临界资源共享时数据竞争操作的开销,操作系统为每个线程都分配了栈,所有数据或者函数调用都会通过push或者pop操作并按照LIFO的方式压入或者弹出堆栈,与此同时CPU还有一个RSP寄存器始终会指向栈顶的位置,保证数据压入或者弹出的准确性。

除了push和pop以外,线程还支持通过call指令将函数压入栈中,例如我们代码中调用function,对应的就是call指令完成function调用,此时对应的function函数地址压入栈中,随后函数调用结束,再通过ret指令返回函数调用前的地址:

因为函数调用涉及多参传入等问题,所以对于传参顺序也有一定的约定,例如在某些调用约定中参数自左向右压入栈中,确保CPU执行函数指令的正确性。但是随着计算机的发展,计算机的寄存器也在原有的8个通用寄存器的基础上增加了R8~R15寄存器,对应函数的参数可以直接存放在CPU的寄存器而非线程栈(内存中),大大提升函数执行效率。

# 线程栈的内存分配策略

线程的栈最大容量为8MB,对应的我们可以通过ulimit指令查看,但操作系统并不是一开始就分配这么大的内存空间,毕竟如果为操作系统成百上千的线程分配这么大一块内存空间作为线程栈,是一种非必要的浪费。默认情况下线程初始情况下仅仅分配几个页面,当空间不足时会触发缺页中断,再动态扩容,直到8MB,如果超了就会抛出stack overflow,这也就是为什么递归调用会时常报错的原因:

sharkchili@xxx-xxx:/tmp$ ulimit -a |grep stack
stack size                  (kbytes, -s) 8192
1
2

当然,线程运行时也会涉及一些中断、异常或者系统调用,为保证操作系统的安全避免重要信息在用户层暴露,线程的内核态调用都会在内核栈中开辟一个4~8KB大小的内核栈作为内核调用的历史记录维护栈。基于此内核栈说明了线程栈对于程序运行的安全性也是至关重要的,在互联网发展早期涉及线程栈的调用,黑客就会利用线程栈回溯的特性,在修改ret指令前修改跳转地址,然后注入恶意指令完成线程的劫持与攻击,这就是大名鼎鼎的缓冲区溢出攻击。

# 用户态线程和内核态线程的探究

关于线程的创建一般是有两种方式,即用户态创建和内核态创建,我们先来说第一种实现方式——用户空间实现线程,创建即线程的计数器、寄存器、堆栈都交由开发者自实现线程包,这种方式有着如下优点:

  1. 可以在不支持线程的操作系统下高效运行
  2. 在用户级空间,可以灵活调控线程切换,在进行任务切换时也不会涉及内核态的调用,更加高效和轻量级
  3. 因为所有的线程数行管理,对应的进程都会维护一份专用的线程表,所以可以灵活的涉及线程的调度算法和策略

唯一的缺陷是这些线程模型设计对于内核是感知不到的,从系统内核角度来看这个线程就是一个单线程进程,无法感知内核态调用的阻塞,即无法感知IO就绪而进行read调用,从而使得这些线程全部阻塞停止:

相比之下内核态线程的创建都在内核态中统一管理一个线程的线程表,在进行系统阻塞式调用时会主动进行线程表的更新工作,同时会根据调度策略从就绪的线程队列中指定一个线程获得CPU时间片。虽然大量线程创建、操作、终止开销相较于用户线程更大,但是内核可以非常方便地检查和管理操作系统中的所有线程,管理更加方便,这也就是目前传统编程中的一对一线程模型:

综上所述,操作系统哲学也提出一种多对多的线程创建理念,即多个用户态线程绑定一个内核态线程,应用层开辟线程组绑定一个内核态线程,使得用户级线程和内核线程构成一个多路复用的关系,既延续了用户级线程的轻量级调度,又拥有了内核级别的系统调用切换管理,通过多对多的绑定避免了大量用户态级别的线程阻塞,又避免了大量线程创建、终止的系统级开销:

# 小结

本文简单介绍了:

  1. 线程的发展和设计理念,介绍了线程的诞生
  2. 线程的寄存器、栈、程序计数器作用
  3. 线程栈LIFO特性和push、pop流程,以及内核栈、缓冲区溢出攻击等问题
  4. 线程之间的关系和操作系统的调度策略
  5. 线程的基本创建、终止以及yield切换和调度以及线程间的阻塞join等待
  6. 线程内核调用阻塞切换的开销以及用户态线程和内核态线程创建的优秀设计理念

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)。

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

# 参考

《趣话计算机底层技术》 《现代操作系统》

一文搞懂【线程】:https://blog.csdn.net/bufangbufang/article/details/144293546 (opens new window) 深入理解Java线程的yield()方法:原理、应用与陷阱:https://blog.csdn.net/weixin_51786043/article/details/147397017 (opens new window)

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
Linux进程管理
进程与线程深度解析

← Linux进程管理 进程与线程深度解析→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
从开源框架中学习那些实用的位运算技巧
03-25
03
浅谈分布式架构设计思想和常见优化手段
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×