使用内核线程实现
使用用户线程实现
广义上来讲,一个线程只要不是内核线程,那就可以认为是用户线程(User Thread,UT),而狭义的用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知到线程存在的实现,用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。如果程序实现得当,这种线程不需要切换到内核态,因此操作可以是非常快速且低消耗的,也可以支持规模更大的线程数量,部分高性能数据库中的多线程就是由用户线程实现的。这种进程与用户线程之间1:N的关系称为一对多的线程模型。
混合实现
混合环境下,既存在用户线程,又存在轻量级进程。用户线程还是完全建立在用户空间中,而操作系统所支持的轻量级进程则作为用户线程和内核线程之间的桥梁。这种混合模式下,用户线程与轻量级进程的数量比是不定的,是M:N的关系。许多Unix系列的系统,都提供了M:N的线程模型实现。
2、java线程与操作系统之间的调度关系
到了后来,操作系统内核纷纷都支持了多线程(windows开始就支持),那么java也要考虑推卸一些责任了,这样java线程就和操作系统线程一一对应(操作系统调度,使用内核线程实现)或多多对应了(操作系统和JVM一起调度,混合实现),这个时候,如果是一一对应,那么线程的调度完全交给了操作系统内核。Linux从内核2.6开始使用NPTL (Native POSIX Thread Library)支持,但这时线程本质上还轻量级进程。 Java里的线程是由JVM来管理的,它如何对应到操作系统的线程是由JVM的实现来确定的。Linux 2.6上的HotSpot使用了NPTL机制,JVM线程跟内核轻量级进程(LWP)有一一对应的关系。线程的调度完全交给了操作系统内核,当然jvm还保留一些策略足以影响到其内部的线程调度,举个例子,在linux下,只要一个Thread.run就会调用一个fork产生一个线程。ava线程在Windows及Linux平台上的实现方式,现在看来,是内核线程的实现方式。这种方式实现的线程,是直接由操作系统内核支持的——由内核完成线程切换,内核通过操纵调度器(Thread Scheduler)实现线程调度,并将线程任务反映到各个处理器上。内核线程是内核的一个分身。程序一般不直接使用该内核线程,而是使用其高级接口,即轻量级进程(LWP),也即线程。其调度关系如下图所示
3、Java 进程与操作系统进程
在 JDK 的代码中,只提供了 ProcessImpl 类来实现 Process 抽象类。其中引用了 native 的 create, close, waitfor, destory 和 exitValue 方法。在 Java 中,native 方法是依赖于操作系统平台的本地方法,它的实现是用 C/C++ 等类似的底层语言实现。我们可以在 JVM 的源代码中找到对应的本地方法,然后对其进行分析。JVM 对进程的实现相对比较简单,以 Windows 下的 JVM 为例。在 JVM 中,将 Java 中调用方法时的传入的参数传递给操作系统对应的方法来实现相应的功能。对应关系如下图:
每个 JVM 中创建的进程都对应了操作系统中的一个进程。但是,Java 为了给用户更好的更方便的使用,向用户屏蔽了一些与平台相关的信息,这为用户需要使用的时候,带来了些许不便。
在使用 C/C++ 创建系统进程的时候,是可以获得进程的 PID 值的,可以直接通过该 PID 去操作相应进程。但是在 JAVA 中,用户只能通过实例的引用去进行操作,当该引用丢失或者无法取得的时候,就无法了解任何该进程的信息。
当然,Java 进程在使用的时候还有些要注意的事情:
- Java 提供的输入输出的管道容量是十分有限的,如果不及时读取会导致进程挂起甚至引起死锁。
- 当创建进程去执行 Windows 下的系统命令时,如:dir、copy 等。需要运行 windows 的命令解释器,command.exe/cmd.exe,这依赖于 windows 的版本,这样才可以运行系统的命令。
- 对于 Shell 中的管道 ‘ | ’命令,各平台下的重定向命令符 ‘ > ’,都无法通过命令参数直接传入进行实现,而需要在 Java 代码中做一些处理,如定义新的流来存储标准输出,等等问题。
总之,Java 中对操作系统的进程进行了封装,屏蔽了操作系统进程相关的信息。同时,在使用 Java 提供创建进程运行本地命令的时候,需要小心使用。
一般而言,使用进程是为了执行某项任务,而现代操作系统对于执行任务的计算资源的配置调度一般是以线程为对象(早期的类 Unix 系统因为不支持线程,所以进程也是调度单位,但那是比较轻量级的进程,在此不做深入讨论)。创建一个进程,操作系统实际上还是会为此创建相应的线程以运行一系列指令。特别地,当一个任务比较庞大复杂,可能需要创建多个线程以实现逻辑上并发执行的时候,线程的作用更为明显。因而我们有必要深入了解 Java 中的线程,以避免可能出现的问题。
从概念上来说,一个 Java 线程的创建根本上就对应了一个本地线程(native thread)的创建,两者是一一对应的。 问题是,本地线程执行的应该是本地代码,而 Java 线程提供的线程函数是 Java 方法,编译出的是 Java 字节码,所以可以想象的是, Java 线程其实提供了一个统一的线程函数,该线程函数通过 Java 虚拟机调用 Java 线程方法 , 这是通过 Java 本地方法调用来实现的。
以下是 Thread#start 方法的示例:
public synchronized void start() { … start0(); … }
可以看到它实际上调用了本地方法 start0, 该方法的声明如下:
private native void start0();
Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(),stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的 . 这个方法放在一个 static 语句块中,这就表明,当该类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。
private static native void registerNatives(); static{ registerNatives(); }
本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,定义了各个操作系统平台都要用到的关于线程的公用数据和操作,如代码清单 2 所示。
清单 2
JNIEXPORT void JNICALL Java_Java_lang_Thread_registerNatives (JNIEnv *env, jclass cls){ (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); } static JNINativeMethod methods[] = { {"start0", "()V",(void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive","()Z",(void *)&JVM_IsThreadAlive}, {"suspend0","()V",(void *)&JVM_SuspendThread}, {"resume0","()V",(void *)&JVM_ResumeThread}, {"setPriority0","(I)V",(void *)&JVM_SetThreadPriority}, {"yield", "()V",(void *)&JVM_Yield}, {"sleep","(J)V",(void *)&JVM_Sleep}, {"currentThread","()" THD,(void *)&JVM_CurrentThread}, {"countStackFrames","()I",(void *)&JVM_CountStackFrames}, {"interrupt0","()V",(void *)&JVM_Interrupt}, {"isInterrupted","(Z)Z",(void *)&JVM_IsInterrupted}, {"holdsLock","(" OBJ ")Z",(void *)&JVM_HoldsLock}, {"getThreads","()[" THD,(void *)&JVM_GetAllThreads}, {"dumpThreads","([" THD ")[[" STE, (void *)&JVM_DumpThreads}, };
到此,可以容易的看出 Java 线程调用 start 的方法,实际上会调用到 JVM_StartThread 方法,那这个方法又是怎样的逻辑呢。实际上,我们需要的是(或者说 Java 表现行为)该方法最终要调用 Java 线程的 run 方法,事实的确如此。 在 jvm.cpp 中,有如下代码段:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) … native_thread = new JavaThread(&thread_entry, sz); …
这里JVM_ENTRY是一个宏,用来定义JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程,其线程函数是 thread_entry,如清单 3 所示。
清单 3
static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); Handle obj(THREAD, thread->threadObj()); JavaValue result(T_VOID); JavaCalls::call_virtual(&result,obj, KlassHandle(THREAD,SystemDictionary::Thread_klass()), vmSymbolHandles::run_method_name(), vmSymbolHandles::void_method_signature(),THREAD); }
可以看到调用了 vmSymbolHandles::run_method_name 方法,这是在 vmSymbols.hpp 用宏定义的:
class vmSymbolHandles: AllStatic { … template(run_method_name,"run") … }
至于 run_method_name 是如何声明定义的,因为涉及到很繁琐的代码细节,本文不做赘述。感兴趣的读者可以自行查看 JVM 的源代码。
相关推荐
"JAVA线程与进程的区别" JAVA语言中,线程(Thread)和进程(Process)是两个基本概念,它们都是操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。但是,它们之间有着本质的区别。 ...
在IT领域,尤其是Java编程中,多线程是不可或缺的一部分,它使得程序能够同时执行多个任务,极大地提高了程序的效率和响应性。本教程“Java的多线程”由孙鑫老师主讲,旨在帮助初学者从入门到精通地掌握这一关键技能...
在命令行中运行`jstack <pid>`(pid是Java进程的ID),即可查看该进程的线程状态。 总结来说,Java提供了多种方式来查看JVM中的线程活动状况,无论是通过编程还是使用命令行工具,都能帮助开发者诊断和优化多线程...
"操作系统中的进程、线程与Java的多线程" 操作系统中的进程是指特定的代码序列在指定数据集上的一次执行活动,是指并行程 序的的一次执行过程。在Windows 95中,就是一个EXE文件的执行过程。这是一个动态概念,具有...
Java并发编程是Java开发中的重要领域,涉及到进程与线程的概念,这两个概念是理解多任务执行的基础。在现代计算机系统中,进程和线程被广泛用于实现高效的资源管理和任务调度。 **1. 进程** 进程是操作系统分配...
`bin`目录可能包含编译后的可执行文件或脚本,用于启动和管理这些Java进程。为了深入了解实现细节,需要查看源代码并理解其逻辑。 总的来说,Java实现守护进程、监听进程以及管理多个进程和JVM涉及多线程、网络编程...
为了获取这些信息,我们可以使用jstack工具,它是Java开发工具包(JDK)的一部分,能够输出Java进程的线程堆栈跟踪信息。 jstack命令通常可以输出以下类型的信息: 1. 线程的完整堆栈跟踪,包括本地方法。 2. 显示...
本文将详细探讨Java中的线程和进程的区别,以及它们在程序设计和运行时的角色和重要性 理解线程和进程的区别对于Java程序员来说至关重要。线程提供了一种高效的方式来实现并发执行,而进程则是操作系统资源分配的...
在计算机科学领域,进程与线程是操作系统中最基础且至关重要的概念。进程是程序执行时的一个实例,每个进程都有自己的独立内存空间,包括代码、数据、堆栈等资源。线程则是进程内的一个执行单元,它共享进程的内存...
进程和线程是计算机操作系统中的两个基本概念,对于任何软件开发者,尤其是系统级或服务器端开发者来说,理解和掌握它们至关重要。下面将详细解释这两个概念,以及它们在实际应用中的作用。 首先,我们来理解“进程...
在Java编程领域,多线程和进程是两个关键的概念,对于任何有志于从事Java开发的程序员来说,理解和掌握它们至关重要。...通过深入学习和实践,开发者能够编写出更加高效、安全的多线程Java应用程序。
《JAVA多线程设计模式》PDF 下载 《Java线程 高清晰中文第二版》中文第二版(PDF) 前言 第一章 线程简介 Java术语 线程概述 为什么要使用线程? 总结 第二章 Java线程API 通过Thread类创建线程 使用Runable接口...
本次实验旨在深入探索Java中多线程处理与多媒体应用的核心技术,具体目标包括: 1. **理解线程概念**:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单元。 2. **线程状态与...
在计算机科学中,程序、进程和线程是操作系统的基础概念,尤其在Java编程语言中,理解和掌握这些概念对于开发高效、并发的软件至关重要。本文将深入探讨Java的多线程特性,以及程序、进程和线程的基本定义和它们之间...
子进程与父进程拥有不同的可执行代码和数据内存空间,而同一进程中多个线程共享数据内存空间,每个线程有自己的执行堆栈和程序执行上下文(Context)。 由于这些区别,线程有时被称为轻型进程(LightWeight Process...
Java雷电游戏是一款基于SWING图形用户界面库和Java进程管理技术开发的纯Java应用程序,旨在帮助开发者加深对Java线程的理解。通过分析这款游戏的源代码,我们可以学习到多个Java编程的重要知识点。 首先,SWING是...