java多线程--线程和线程池
线程池知识梳理
linux操作系统进程和线程:(待完善)
小企鹅镇楼
先从最基本的概念慢慢往下看
首先打开电脑电源后,cpu 执行bios(basic input output system,一段烧在计算机主板里的代码),此时没有内存,没有磁盘。
bios做一些基本的硬件检测等操作,读取存储(光盘或者磁盘等)里一个扇区的代码(512字节)加载到内存的0x7c000位置,cpu然后从内存呢这个位置开始执行。
这512个字节就是bootloader(常见的有grub,lilo等),bootloader 操作系统的代码引入进来,后续会cpu跳转保护模式,硬件初始化,硬件检查,文件系统初始化等等。
这部分内容可以推荐一本 《自己动手写操作系统》,完全从零开始写操作系统,操作系强。
由此可见,在电脑的硬件层没有进程或者线程的概念,bios导入代码,cpu从固定位置执行,被执行的代码继续引入代码和数据,cpu继续执行。
如果这里动手写过代码,就会发现,基本都是汇编语言编写,所有的操作都是直接操作硬件,非常非常繁琐,无法完成大规模的工作,而且很多底层的操作完成是电脑硬件的操作
难度大,容易出错且重复性非常高,所以先驱们把常用的操作写成一些汇编的方法供调用,慢慢就是操作系统的概念了 ,从Dos(DiskOperationSystem)到多线程操作系统Unix,linux,再到有图形的操作系统。
再看Linux操作系统,linux操作系统教程一般包含 内存管理,进程管理和cpu调度资源分配,文件管理,网络等外部设备管理,内存是直接寻址cpu指令直接(load)获取数据,直接给cpu提供指令(方法端)给寄存器提供数据(数据段)。文件操作系统屏蔽了磁盘的基本操作,以文件的形式保存数据,方便读写,其他外部设备也是屏蔽了硬件细节,提供统一接口。
我们接下来看下进程数据结构和调度方法,sched.h文件里 struct task_struct,进程是一个数据结构,标识进程的一些属性(名称,id,优先级,状态等),操作系统维护一个进程表,指向所有的进程,系统进程启动时创建并且运行。用户进程以其为父进程,创建后为Runnable 状态等待调度,调度后变运行状态直到运行结束或者主动(等待资源,sleep等)或者被动结束(时间片用完或者别其他高优先级进程抢占等)线程是轻量级的,可以一个进程创建多个线程,线程内部部分资源可以共享,进程比较重,线程比较轻。同一进程下的多个线程是可以在多核CPU下并行运行的。但2.4内核及以前的系统实现的线程没有内核支持,无法在多核的情况下并行运行。
记得原来考虑过如果使用一个操作系统的进程实现多线程的效果,可以自己模拟操作系统的一整套多线程机制,自己编写一个进程对象,一个进程管理表。主流程扫描表,按照时间片或者其他方式轮流执行每个“进程” 的代码,执行完毕后保留现场,到下一次再被执行时初始化现场,新建进程时加入管理表,进程结束后删除记录。而且单cpu场景下,cpu本来只能同时做一个指令,宏观上是多线程,微观上其实还是单线程的。这里是凭印象写的,后续有时间再详细整理,linux系统博大精深这里只是抛砖引玉。
linux 进程例子:
Linux线程例子:
Java的线程(待完善):
java内部使用线程机制,可以运行一个java程序用jps看只增加一个进程(main函数执行时,进程名称为main), jstack 进程id ,看到所有线程。
可以打开java的源码 java.lang.Thread ,java部分代码比较容易理解,但是涉及到的操作系统知识较多,后续再补充
Java线程池:
线程池接口 :
ExecutorService 接口,从接口看线程池提供的全部功能。
ThreadPoolExecutor
ForkJoinPool
原理: 线程可回收利用,使用了blockingqueue(blockingqueue使用了 锁里的Condition)
基本流程: submit runnable 后 判断当前worker是否大于等于 coreSize,如果没有,新建worker,锁自己执行runnable,如果到了,放blockingqueue,blockingqueue满了,如果size小于max,新建worker。
worker执行完runnable,判断是否大于coresize,大于等待一段时间后销毁自己,否则取读blockingqueue的runnbale,读不到则被block
shutdown 方法 是 要获取worker锁, 这样在运行的worker会跑完,然后线程标识位置为 interupt
shutdownnow 直接置为interupt,不过置为interupt也不知直接线程就停止了,遇到判断标志位的才停止,例如wait和方法内部的判断等。