第十一章
11.1 线程
11.1.1 线程的概念
说起线程,首先得提起进程,相信很面试者在回答进程与线程的区别时都会用一句话:“进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位”,只能说这句话部分正确,就目前来讲,一般操作系统(这里指Linux)的任务调度执行单位确实是线程,但是未来可能会支持以协程为单位的任务调度。再说进程是操作系统资源分配的基本单位
这句话,在linux操作系统中,当需要创建一个进程时,通过系统调用fork()然后再调用copy_process来完成,创建进程时,会创建并分配该进程所需要的任务资源和内存,最终形成图11-1
的内存模型,粗略分为代码块(存放代码数据)、数据块(涉及数据的存放)、堆(进程malloc动态分配内存区)、用户栈(进程执行时动态存储区)、共享库存储映射区(主要是映射动态库或其他依赖库)等,这些区域在进程创建时都是要先创建的(虚拟空间),这些虚拟空间都是要占用一定内存的,也就是所谓的进程的资源分配的一部分,当然还有其他资源的分配,比如页目录、页表等。然而,这个时候线程去哪里了呢,其实,这个时候同时创建出了一个线程(概念上的),最终形成一进程一线程模型。如果该进程在执行的过程中再通过pthread_create()函数创建另外一些线程,那就形成了一进程多线程模型。由于linux底层线程的创建函数,底层也是copy_process()函数来完成的,实现上创建过程跟fork()创建进程是一样的,只是创建线程时,不会再创建图11-1
描述的空间,而是继承发起线程创建者的这块空间。最终总结线程和进程的区别:
1、进程和线程创建过程本质上是没有区别的,最终都是通过copy_process()函数调用
2、进程只是在概念上包含了线程,以进程为调度单位时,本质上就是一进程一线程的模型在运行,早期的linux调度模型就是这样的
3、进程的概念实际上就是早期以进程为调度单位提出的,所以从技术上讲,没什么区别,只是人为制定的概念加以区分
图11-1
11.1.2 Hotspot线程
在执行命令 java [options] xxx.class param1 param2 ... paramn
时,实际上Linux是fork()出了一个新的Java进程来执行后续操作,并且在执行的过程中又会创建其他的线程(Java线程、VM线程、GC线程等)。然而在Java执行的过程中,是需要Java线程与VM双向交互的,例如:在线程的启停时需要执行一些必要的任务(比如保存安全点),以及垃圾收集期间线程的协调和同步,所以Hotspot JVM需要管理这些线程,但是线程又是有操作系统创建的,不是JVM创建的,因此在Hotspot中,每个Java线程都会映射到一个操作系统线程,同时需要跟踪和管理这些线程,确保Hotspot内部的状态与线程本身状态同步。
11.1.3 线程关系
Hotspot中,各线程类的关系图,如图11-2
图11-2
11.1.4 线程的属性
属性名 | 功能描述 | 归属类 |
---|---|---|
_pending_exception | 指向当前线程等待处理的异常对象的引用 | ThreadShadow |
_exception_file | 指向异常发生时的文件信息 | ThreadShadow |
_exception_line | 指向异常发生时的代码行信息 | ThreadShadow |
_real_malloc_address | 指向线程创建时分配的实际地址的首地址 | Thread |
_SR_lock | 线程状态更改的监视器 | Thread |
_suspend_flags | 线程挂起标志 | Thread |
_num_nested_signal | 进入线程内部处理linux系统信号的个数,处理完成后会-1 | Thread |
_active_handles | 指向当前线程持有的JNI句柄链表头指针 | Thread |
_free_handle_block | 空闲JNI句柄块的链表头指针 | Thread |
_last_handle_mark | HandleMark的指针,该Mark会记录指向线程本身、存储区域、存储chunk等信息 | Thread |
_tlab | 线程本地分配缓存 | Thread |
_allocated_bytes | 当前线程在Java堆中已分配的字节数 | Thread |
_vm_operation_started_count | 记录已开始的VM操作数量 | Thread |
_vm_operation_completed_count | 记录已完成的VM操作数量 | Thread |
_osthread | 指向基于平台的线程实现 | Thread |
_resource_area | 线程本地的用于临时分配的资源区域 | Thread |
_handle_area | 线程本地的用于分配JNI句柄的资源区域 | Thread |
_stack_base | 线程栈基址 | Thread |
_stack_size | 线程栈大小 | Thread |
_ParkEvent | 线程阻塞时的事件对象 | Thread |
_SleepEvent | 线程睡眠时的事件对象 | Thread |