摘要
由于Java的并发和线程息息相关,我们今天看一下线程的实现方式轻量级,通用的线程实现方式有:
- 使用内核线程实现
- 使用用户线程实现
- 使用用户线程和轻量级进程实现
- Java线程实现
1. 内核线程
内核线程(KLT)其实就是直接由操作系统内核支持的线程,这种线程由内核完成切换。内核通过调度器对线程进行调度,将线程映射到处理器上。
在我们程序的实际使用中,不会直接去使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(LWP),轻量级进程就属于我们常说的线程。轻量级进程和内核线程是1:1的关系。
由于内核线程的支持,每一个轻量级进程都是一个可以独立的调用单元,由于轻量级进程基于内核线程实现,内核线程直接由操作系统控制,所以我们可以不用线程的创建、切换、调度、阻塞,几乎所有的线程操作都可以交由操作系统内处理。
轻量级线程的缺点就是,我们的操作系统如果要使用轻量级进程必须要进行系统调用,系统调用需要在用户态和内核态进行切换,调用代价相对较高。其次每个轻量级进程对应一个内核线程,因此会占用一定的内核资源,因此一个操作系统支持轻量级进程的数量是有限的。
2. 用户线程
用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知线程的存在。用户线程的所有操作都在用户态中完成,不需要内核的帮助。
用户线程的优点是不需要进行系统调用,调用代价低,缺点就是用户需要自己维护线程的创建、调度、同步、阻塞等操作,实现代价相对较高。
3. 用户线程+轻量级进程
用户线程+轻量级进程就是采用了上述两种实现的结合,通过这种结合我们可以取其精华去其糟粕。
首先用户线程是完全建立用户空间,用户线程的创建、切换调用代价较低,并且可以支持大规模的并发。
轻量级进程是用户线程和内核线程的之间的桥梁,通过轻量级进程,我们可以使用内核提供的线程调度功能和处理器映射(实现代价相对较高的操作)。在这种混合模式中,用户线程和轻量级进程的比例关系为N:M
4. Java线程实现
Java线程的实现方式就是采用用户线程+轻量级进程混合的模式,Java线程和轻量级进程的比例关系为1:1,至于为什么是1:1,我们可以分析一下:
我们在使用Java的Thread时,会调用start方法开启一个线程。
????public?synchronized?void?start()?{
????????/**
?????????*?This?method?is?not?invoked?for?the?main?method?thread?or?"system"
?????????*?group?threads?created/set?up?by?the?VM.?Any?new?functionality?added
?????????*?to?this?method?in?the?future?may?have?to?also?be?added?to?the?VM.
?????????*
?????????*?A?zero?status?value?corresponds?to?state?"NEW".
?????????*/
????????if?(threadStatus?!=?0)
????????????throw?new?IllegalThreadStateException();
????????/*?Notify?the?group?that?this?thread?is?about?to?be?started
?????????*?so?that?it?can?be?added?to?the?group's?list?of?threads
?????????*?and?the?group's?unstarted?count?can?be?decremented.?*/
????????group.add(this);
????????boolean?started?=?false;
????????try?{
????????????start0();
????????????started?=?true;
????????}?finally?{
????????????try?{
????????????????if?(!started)?{
????????????????????group.threadStartFailed(this);
????????????????}
????????????}?catch?(Throwable?ignore)?{
????????????????/*?do?nothing.?If?start0?threw?a?Throwable?then
??????????????????it?will?be?passed?up?the?call?stack?*/
????????????}
????????}
????}
????private?native?void?start0();
在start方法中有个关键的方法就是start0(),然后该方法是个native方法,这时候我们就需要看一下JDK的源码实现了,关于这个start0方法是在源码文件./jdk/src/share/native/java/lang.Thread.c中,如下图:
我们可以看到,start0方法实际上执行的JVM_StartThread方法,该方法所在的文件是./hotspot/src/share/vm/prims/jvm.cpp。
上图中JVM_StartThread方法中我们会new一个JavaThread,然后我们看一下这个JavaThread的实现, JavaThread的类定义在./hotspot/src/share/vm/runtime/thread.cpp中,如下图:
os::create_thread(this, thr_type, stack_sz);这里就是各个操作系统去创建线程了,这里我们假设我们的操作系统是linux操作系统,create_thread的方法存储在,./hotspot/src/os/linux/vm/os_linux.cpp中,linux中的线程创建方式是pthread,在create_thread方法中最终会有如下代码,完成轻量级进程的创建,如下:
通过上述分析,我们可以看到,在Java中每创建一个线程,我们变会创建一个轻量级进程,所以Java中的用户线程和轻量级进程为1:1。
本期的Java线程实现介绍到这,我是shysh95,我们下期再见!!!
本文暂时没有评论,来添加一个吧(●'◡'●)