本书由科大讯飞高级系统架构师撰写,结合大量源码与图示,通俗易懂;自顶向下解析JDK、JVM、Linux中的Java线程通信机制、同步机制、锁机制、原子计数器、线程安全容器、线程池的实现原理与应用技巧。全书共12章,分为三篇。
基础篇(第1~5章),从Linux的线程基础讲起,重点从JDK、JVM、Linux多个维度讲解Java线程的设计、通信与同步机制,如内存一致性、内存屏障、多线程间数据通信、并发控制等核心知识,从而在实际开发中提高程序的性能和稳定性。
进阶篇(第6~9章),从CPU的架构讲起,重点讲解锁算法(MCS、CLH、AQS)、Java锁机制、原子计数器、线程并发容器、线程池的设计原理与实现,以期让读者掌握Java线程的进阶知识,不仅能开发出高性能的程序,而且遇到问题可以举一反三,找到最佳方案。
应用篇(第10~11章),详细讲解Java线程的常见模型与使用技巧。以电商真实场景为例,从面临的挑战、基于Java多线程的实现方案与优化等角度层层递进,让读者感受真实的Java线程“威力”。 最后,总结多线程编程的常见问题与使用技巧。
本书由科大讯飞高级系统架构师撰写,结合大量源码与图示,通俗易懂;自顶向下解析JDK、JVM、Linux中的Java线程通信机制、同步机制、锁机制、原子计数器、线程安全容器、线程池的实现原理与应用技巧。
Preface 前 言
为什么要写这本书
2002年的秋天,一个偶然的机会我接触到了计算机编程。我编写的第一个程序成功运行所带来的新奇与成就感,让我对计算机充满了热爱。在高考填报志愿时,我毫不犹豫地选择了计算机,憧憬着未来成为一名出色的工程师。
2021年我36岁生日那天,在与家人聊天的时候,我发现自己在不知不觉中已经与Java“共事”近10年了。在十多年的技术求索之路上,王国维先生的三重人生境界不断激励着我前行:
古今之成大事业、大学问者,必经过三种之境界:“昨夜西风凋碧树,独上高楼,望尽天涯路。”此第一境也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境也。“众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。”此第三境也。
一个技术人员要想成为一名真正的工匠,就必须耐得住寂寞,经得起时间的考验,并且始终保持内心深处的热爱。
回顾过去十多年的技术之路,我得到了很多师长的提携与指点,受到100多本技术书籍的“滋养”。正所谓“前人栽树,后人乘凉”,我也想为Java社区略尽微薄之力。
十多年来,我一直深耕互联网领域的系统研发,在Java多线程编程方面积累了一定的经验。在工作之余,我也会和技术社区中的朋友交流Java多线程技术,发现很多朋友在技术细节上存在诸多困惑。而目前大部分书籍和资料多以功能介绍为主,很少有剖析Java线程的设计原理的。所以,我决定写一本书,旨在从Linux、JVM、Java多个方面来详细阐述Java线程的技术原理与实现细节。
本书将为读者提供一个全新的视角,秉承“大道至简”的主导思想,只介绍Java多线程开发中最值得关注的内容,致力于底层实现原理的分析,而非API的使用。
本书特色
随着互联网的发展,会有越来越多的公司进行数字化转型。在数字化转型的过程中,高并发、高性能是衡量系统性能的核心指标,越来越多的公司对从业人员的多线程编程能力提出了更高的要求。本书将打通Java、JVM、Linux的全链路技术栈,剖析Java多线程的实现原理,以便读者厘清现象与本质。同时,本书结合实际业务场景沉淀出多线程编程模型,以便读者快速获得多线程编程能力。
本书中的一些实操例子,开发工程师可直接应用于实际业务场景中;设计原理和深入分析的内容,可帮助架构师拓展解决问题的思路;工具和问题分析的内容,可帮助技术人员诊断线上环境中的系统问题。
读者对象
Java开发工程师
系统架构师
运维工程师
并发编程爱好者以及其他对Java技术感兴趣的人员
如何阅读本书
本书从Java多线程基础知识、进阶功能、实际应用三个维度展开讨论,分为三篇,共11章内容。
基础篇(第1~5章),着重讲解Java线程的基础知识、设计原理与具体实现。
第1章 从Linux线程基础出发,详细讲解Linux系统的进程、线程、任务调度等概念,并详细阐述Linux线程库Pthread的使用。
第2章 详细讲解JVM的逻辑架构,涵盖JVM的线程模型、JNI访问等组成部件。
第3章 详细讲解Java线程的基本概念、多线程带来的问题、Java内存模型,以及JVM内存屏障的实现、JVM线程的底层实现与生命周期等。
第4章 详细讲解Java线程的睡眠、等待、中断等线程通信机制的设计原理与JVM实现。
第5章 详细讲解Java的synchronized、volatile、CAS等同步控制机制的设计原理与JVM实现。
进阶篇(第6~9章),着重讲解Java多线程的高阶功能的实现原理。
第6章 详细讲解锁的演进过程、Java的AQS设计原理以及常见锁机制的具体实现。
第7章 详细讲解Java的各种原子操作类的设计原理与具体实现。
第8章 详细讲解Java的List、Map、Queue等线程安全的并发容器的设计原理与具体实现。
第9章 详细讲解Java中常见的线程池的设计原理与具体实现。
应用篇(第10、11章),着重讲解Java多线程的常见模型与编程技巧。
第10章 总结Java线程池的几种常见使用模型,以及在业务场景中的实现案例。
第11章 总结多线程编程中的常见问题,并给出应对这些问题的编程技巧。
其中,第3~8章为本书的重点章,如果你没有充足的时间完成全书的阅读,可以选择阅读这些重点章。如果你是一名初学者,请在开始本书阅读之前,先学习一些Java多线程的基础理论知识。
勘误和支持
由于写作水平有限,编写时间仓促,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果你有更多的宝贵意见,可以通过微信sky_ccy与我沟通。同时,你也可以通过邮箱easyjavathread@gmail.com与我联系。期待得到你的真挚反馈,让我们在技术之路上互勉共进。
致谢
感谢知名操作系统专家彭东,他帮忙审校了第1章,并对该章内容提出了一些非常中肯的建议。
特别感谢我的太太林燕女士、女儿可歆和儿子乔治,我为写作这本书牺牲了很多陪伴他们的时间,但也正因为有了他们的付出与支持,我才能坚持写下去。
同时,感谢在我成长路上帮助过我的领导、师长、朋友,是他们的指导让我成了更好的自己。
谨以此书献给我最亲爱的家人,以及众多热爱Java技术的朋友们!
储诚益
储诚益
科大讯飞高级系统架构师,致力于研究下一代工业互联网系统架构、人工智能、中小制造企业数字化转型。拥有十余年的IT领域工作经验,曾就职于多家知名IT企业并负责技术架构设计和管理工作,积累了丰富的分布式架构、大数据、云计算以及大规模IT系统等的建设、实施、咨询、管理经验。
目 录 Contents
前言
基础篇
第1章 Linux线程基础2
1.1 Linux进程2
1.1.1 深入理解进程2
1.1.2 进程描述符3
1.1.3 进程创建6
1.1.4 上下文切换7
1.2 Linux进程间通信7
1.2.1 信号 8
1.2.2 管道9
1.2.3 共享内存 9
1.2.4 FIFO队列 9
1.2.5 消息队列 10
1.2.6 Socket10
1.3 CPU任务调度10
1.3.1 实时进程与普通进程11
1.3.2 实时调度策略11
1.3.3 普通调度策略11
1.3.4 CFS调度算法11
1.3.5 整体任务调度12
1.4 Linux线程13
1.4.1 Pthread简介14
1.4.2 线程创建16
1.4.3 线程终止18
1.5 线程同步:互斥量20
1.5.1 创建互斥量21
1.5.2 互斥量解锁22
1.5.3 mutex示例22
1.6 线程同步:条件变量23
1.6.1 创建条件变量23
1.6.2 条件变量等待24
1.6.3 条件变量通知24
1.6.4 条件变量使用示例24
1.7 线程同步:信号量25
1.7.1 初始化未命名信号量26
1.7.2 等待一个信号量26
1.7.3 发布一个信号量26
1.8 小结27
第2章 JVM基础知识28
2.1 Java、JDK、JRE与JVM28
2.2 Java跨平台原理29
2.3 JVM系统架构30
2.3.1 类加载子系统30
2.3.2 运行时数据区33
2.3.3 执行引擎35
2.4 JVM与操作系统的线程模型36
2.4.1 操作系统的线程模型36
2.4.2 JVM的线程模型38
2.5 JNI机制39
2.5.1 JNI开发流程40
2.5.2 JNI数据类型转换41
2.5.3 实现案例43
2.6 小结43
第3章 JVM线程44
3.1 为什么需要多线程44
3.1.1 CPU访问各组件周期45
3.1.2 多线程的出现45
3.2 多线程带来的问题46
3.2.1 CPU缓存导致的可见性问题46
3.2.2 线程上下文切换带来的原子性
问题47
3.2.3 优化带来的乱序问题48
3.3 Java内存模型与线程规范50
3.3.1 变量共享51
3.3.2 变量共享的内存可见性51
3.3.3 Happens-Before规则52
3.4 内存一致性协议56
3.4.1 CPU缓存读取策略57
3.4.2 CPU缓存写入策略58
3.4.3 MESI协议59
3.5 内存屏障61
3.5.1 内存读写屏障64
3.5.2 内存屏障的实现64
3.5.3 JVM内存屏障指令实现65
3.6 JVM的线程66
3.7 Java线程创建过程68
3.7.1 线程创建69
3.7.2 线程执行73
3.8 Java线程生命周期75
3.8.1 Java线程生命周期模型75
3.8.2 查看线程的状态78
3.9 小结79
第4章 JVM线程通信原理80
4.1 ParkEvent原理80
4.1.1 Allocate方法81
4.1.2 Rlease方法81
4.1.3 park方法82
4.1.4 unpark方法83
4.2 Parker实现原理84
4.2.1 park方法84
4.2.2 unpark方法86
4.3 sleep方法实现原理86
4.3.1 JVM_Sleep函数86
4.3.2 sleep函数87
4.4 ObjectMonitor实现原理88
4.4.1 数据结构89
4.4.2 ObjectMonitor的构造函数89
4.4.3 ObjectWaiter源代码90
4.4.4 TryLock方法91
4.4.5 EnterI方法91
4.4.6 ReenterI方法93
4.4.7 enter方法94
4.4.8 exit方法?95
4.4.9 ExitEpilog方法97
4.5 wait与notify方法实现原理97
4.5.1 设计原理98
4.5.2 wait方法实现原理100
4.5.3 notify方法实现原理102
4.5.4 notifyAll方法实现原理103
4.6 yield方法实现原理103
4.7 join方法实现原理104
4.7.1 JVM_IsThreadAlive105
4.7.2 ensure_join106
4.8 stop方法实现原理107
4.8.1 JVM_StopThread107
4.8.2 HAS_PENDING_EXCEPTION108
4.9 interrupt方法实现原理109
4.10 小结110
第5章 JVM线程同步机制111
5.1 Mark Word 111
5.1.1 Mark Word详解 112
5.1.2 hashCode验证113
5.1.3 轻量级锁的状态信息114
5.1.4 重量级锁的状态信息114
5.2 synchronized设计原理115
5.2.1 synchronized的使用116
5.2.2 synchronized的具体设计118
5.3 synchronized源码分析120
5.3.1 ACC_SYNCHRONIZED解析
过程120
5.3.2 monitorenter指令解析过程122
5.3.3 monitorexit指令解析过程123
5.3.4 锁获取实现过程124
5.3.5 锁释放实现过程126
5.4 volatile实现原理128
5.4.1 实现原理概述129
5.4.2 getfield指令实现过程130
5.4.3 putfield指令实现过程131
5.5 volatile伪共享132
5.6 CAS硬件同步原语134
5.6.1 CAS硬件原语134
5.6.2 JVM CAS实现135
5.6.3 ABA问题135
5.7 Unsafe功能介绍136
5.7.1 操作内存136
5.7.2 获取字段内存偏移量137
5.7.3 普通字段的读取与赋值138
5.7.4 volatile字段的读取与赋值138
5.7.5 CAS操作能力139
5.7.6 线程阻塞与唤醒139
5.7.7 内存屏障139
5.8 Unsafe实现原理140
5.8.1 volatile字段读取140
5.8.2 volatile字段写入141
5.8.3 CAS操作能力141
5.8.4 线程阻塞与唤醒142
5.9 LockSupport实现原理143
5.9.1 Unsafe初始化143
5.9.2 无阻塞对象方法144
5.9.3 有阻塞对象方法144
5.9.4 线程唤醒146
5.10 小结146
进阶篇
第6章 Java锁实现原理148
6.1 CPU架构148
6.1.1 SMP148
6.1.2 NUMA149
6.1.3 SMP与NUMA比较150
6.2 自旋锁的诞生150
6.2.1 SPIN ON TEST-AND-SET150
6.2.2 TEST-AND