`

Thread线程与Process进程

阅读更多
最重要的一个概念
一个线程thread是一个进程process中的一个执行流程,简单的理解:一个进程相当于多个线程的集合,一个进程至少包含一个线程。


Definition定义
-------------
Process
进程是应用程序的一次运行活动;
从操作系统核 心角度来说,进程是操作系统分配和调度系统内存资源、cpu时间片等资源的基本单位,为正在运行的应用程序提供运行环境。

Thread
线程是程序内部有并发性的顺序代码流。是cpu调度资源的最小单元。


Units单位大小
------------
Process
进程是操作系统分配和调度系统内存资源、cpu时间片 等资源的基本单位;一个进程至少包括一个线程。进程是操作系统资源管理的实体。
Thread
线程是cpu调度资源的最小单元。线 程是进程的实体。


Resource系统资源分配上
-------------
Process
每个进程都有自己的内存地址空间。
Thread
线 程没有自己独立的内存资源,它只有自己的执行堆栈和局部变量。但是在同属一个进程的多个线程中他们可以共享进程的内存资源。

Running执行过程中
-------------
执行过程中,进程有内存单元的初始入口点,在存活阶段里拥有独立的地址空间。
A process has the initial entrance of Memory Units and room of address.

进程是应用程序的一次运行活动,独立地执行;所以某一个进程崩溃以后,在保护模式下不会影响其他的进程,健壮性好。
A process is activity of application.

父进程与子进程 的关系待研究深入中……

每个已创建的进程都可以创建进程,创建进程的进程称为父进程,被创建的新进程为子进程,这样便形成一个进程树。父进程与子进程可并行执行;父进程等 待子进程终止执行。父进程终止后,所有的子进程也都必须要终止。

Thread
而线程不能独立地执行,它必须依附在一个运行中的应用程序上。
但是,同一个进程中的多个线程可以并发地执行,并发性 高,系统在数据交换上花费的资源少,运行效率高。

主线程与其他线程 的关系待研究深入中……

每个进程里都会有一个主线程,由它创建其他线程。

======================================================

Common ground 共同点
--------------
Process和Thread都有生命周期:
create 创建,ready就绪,running运行,waitSleepJoin阻塞,suspend挂起,stoped死亡

--------------
代码解析
Process
Java 进程的建立方法
在 JDK 中,与进程有直接关系的类为 Java.lang.Process,它是一个抽象类。在 JDK 中也提供了一个实现该抽象类的 ProcessImpl 类,如果用户创建了一个进程,那么肯定会伴随着一个新的 ProcessImpl 实例。同时和进程创建密切相关的还有 ProcessBuilder,它是在 JDK1.5 中才开始出现的,相对于 Process 类来说,提供了便捷的配置新建进程的环境,目录以及是否合并错误流和输出流的方式。
Java.lang.Runtime.exec 方法和 Java.lang.ProcessBuilder.start 方法都可以创建一个本地的进程,然后返回代表这个进程的 Java.lang.Process 引用。
Runtime.exec 方法建立一个本地进程
该方法在 JDK1.5 中,可以接受 6 种不同形式的参数传入。
Process exec(String command)
Process exec(String [] cmdarray)
Process exec(String [] cmdarrag, String [] envp)
Process exec(String [] cmdarrag, String [] envp, File dir)
Process exec(String cmd, String [] envp)
Process exec(String command, String [] envp, File dir)
他们主要的不同在于传入命令参数的形式,提供的环境变量以及定义执行目录。
ProcessBuilder.start 方法来建立一个本地的进程
如果希望在新创建的进程中使用当前的目录和环境变量,则不需要任何配置,直接将命令行和参数传入 ProcessBuilder 中,然后调用 start 方法,就可以获得进程的引用。
Process p = new ProcessBuilder("command", "param").start();
也可以先配置环境变量和工作目录,然后创建进程。
ProcessBuilder pb = new ProcessBuilder("command", "param1", "param2");
Map<String, String> env = pb.environment();
env.put("VAR", "Value");
pb.directory("Dir");
Process p = pb.start();

Java 创建线程的方法

实际上,创建线程最重要的是提供线程函数(回调函数),该函数作为新创建线程的入口函数,实现自己想要的功能。Java 提供了两种方法来创建一个线程:

    继承 Thread 类

class MyThread extends Thread{
public void run() {
System.out.println("My thread is started.");
}
}

实现该继承类的 run 方法,然后就可以创建这个子类的对象,调用 start 方法即可创建一个新的线程:

MyThread myThread = new MyThread();
myThread.start();

    实现 Runnable 接口

class MyRunnable implements Runnable{
public void run() {
     System.out.println("My runnable is invoked.");
}
}

实现 Runnable 接口的类的对象可以作为一个参数传递到创建的 Thread 对象中,同样调用 Thread#start 方法就可以在一个新的线程中运行 run 方法中的代码了。

Thread myThread = new Thread( new MyRunnable());
myThread.start();

可以看到,不管是用哪种方法,实际上都是要实现一个 run 方法的。 该方法本质是上一个回调方法。由 start 方法新创建的线程会调用这个方法从而执行需要的代码。 从后面可以看到,run 方法并不是真正的线程函数,只是被线程函数调用的一个 Java 方法而已,和其他的 Java 方法没有什么本质的不同。
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 的源代码。
图 1. Java 线程创建调用关系图
图 1. Java 线程创建调用关系图

综上所述,Java 线程的创建调用过程如 图 1 所示,首先 , Java 线程的 start 方法会创建一个本地线程(通过调用 JVM_StartThread),该线程的线程函数是定义在 jvm.cpp 中的 thread_entry,由其再进一步调用 run 方法。可以看到 Java 线程的 run 方法和普通方法其实没有本质区别,直接调用 run 方法不会报错,但是却是在当前线程执行,而不会创建一个新的线程。

回页首
Java 线程与操作系统线程从上我们知道,Java 线程是建立在系统本地线程之上的,是另一层封装,其面向 Java 开发者提供的接口存在以下的局限性:
线程返回值
Java 没有提供方法来获取线程的退出返回值。实际上,线程可以有退出返回值,它一般被操作系统存储在线程控制结构中 (TCB),调用者可以通过检测该值来确定线程是正常退出还是异常终止。
线程的同步
Java 提供方法 Thread#Join()来等待一个线程结束,一般情况这就足够了,但一种可能的情况是,需要等待在多个线程上(比如任意一个线程结束或者所有线程结束才会返回),循环调用每个线程的 Join 方法是不可行的,这可能导致很奇怪的同步问题。
线程的 ID
Java 提供的方法 Thread#getID()返回的是一个简单的计数 ID,其实和操作系统线程的 ID 没有任何关系。
线程运行时间统计
Java 没有提供方法来获取线程中某段代码的运行时间的统计结果。虽然可以自行使用计时的方法来实现(获取运行开始和结束的时间,然后相减 ),但由于存在多线程调度方法的原因,无法获取线程实际使用的 CPU 运算时间,因而必然是不准确的。
分享到:
评论

相关推荐

    python线程与进程实现方式

    同样的思路可以应用于进程,只需将`threading.Thread`替换为`multiprocessing.Process`。 总结起来,理解Python中的线程、进程和协程是进行并发编程的关键。生成器和装饰器提供了强大的工具,使我们能够以简洁的...

    Android下线程与进程

    在Android系统中,线程(Thread)和进程(Process)是理解应用程序运行机制的关键概念。它们决定了应用如何分配资源和执行任务,对于优化性能、提高用户体验具有重要意义。 **线程** 线程是程序中的执行流,是操作...

    JAVA线程与进程的区别

    但是进程与线程的重要区别在于线程不能够单独执行,它必须运行在处于活动状态的应用程序进程中。 在 Java 语言中,线程支持与语言运行环境结合在一起,提供了多任务并发执行,多线程的意义在于一个应用程序的多个...

    多线程与多进程.rar

    本资源"多线程与多进程.rar"显然提供了关于如何在Python中实现多线程和多进程的实例代码。 首先,我们来理解这两个核心概念: 1. **多线程**:线程是程序中的执行流,每个线程都有自己的程序计数器、系统栈和局部...

    Process--thread-enumeration.rar_delphi thread_delphi枚举进程_进程的线程

    首先,我们要理解进程与线程的基本概念。进程是程序在内存中的运行实例,它拥有独立的资源,如内存空间、文件句柄等。而线程则是进程中的执行单元,是程序执行的最小单位,共享同一进程的资源。在多线程环境下,进程...

    多线程与多进程1

    3. 多进程与多线程的使用: Python提供了`threading`模块来创建和管理线程,而`multiprocessing`模块用于创建进程。`threading`模块提供了Thread类,可以创建并控制线程的执行,例如`thread.start_new_thread()`...

    基于c#的简单的进程与线程管理器

    本文将详细讲解如何使用C#语言来创建一个简单的进程与线程管理器,适合初学者入门。 首先,让我们了解基础概念。进程是操作系统中正在运行的一个程序实例,每个进程都有独立的内存空间,而线程则是进程内的执行单元...

    线程和进程的区别.txt

    在现代操作系统(如Windows、UNIX等)中,进程(Process)和线程(Thread)是两个非常重要的概念,它们对于理解和设计多任务操作系统及其应用程序至关重要。进程是操作系统进行资源分配和调度的基本单位,而线程则是...

    浅析Python多线程与多进程的使用

    `multiprocessing`提供了类似`threading`模块的接口,如Process、Pool等,便于管理和协调多个进程。由于进程间不共享内存,它们之间的通信需要通过进程间通信(IPC)机制,如队列、管道、共享内存等。 在选择多线程...

    Python多线程与多进程笔记1

    "Python多线程与多进程笔记1" Python 多线程和多进程是 Python 编程中两个重要的概念,它们分别对应着不同的并发编程模型。在本文中,我们将详细介绍 Python 中的多线程和多进程,并对比它们的异同。 多进程 多...

    PHP实现多线程多进程

    - **多线程与多进程的选择**: - 如果需要在不同进程之间共享大量数据或状态,使用多线程可能更合适。 - 如果程序需要高度的稳定性和安全性,使用多进程更好。 综上所述,虽然 PHP 并不是设计用来进行多线程编程...

    C#线程进程操作

    本文将详细探讨"操作系统实验之线程与进程"这一主题,主要基于C#编程语言。 首先,我们需要理解进程和线程的基本定义。**进程**是操作系统资源分配的基本单位,它拥有独立的内存空间,包括代码、数据和堆栈。每个...

    内核线程和进程的区别

    这种机制与传统的线程模型不同,传统的模型中线程是系统级资源管理的基本单位,而Linux将线程视作和普通进程一样,通过共享部分资源的方式来实现多线程操作。 在Linux内核中,并没有专门的线程调度算法或者数据结构...

    MFC进程与线程Demo程序.zip

    在这个"MFC进程与线程Demo程序"中,我们可以深入理解并实践如何在MFC环境中管理进程和线程。 首先,进程是操作系统分配资源的基本单位,而线程则是执行的基本单位。在MFC中,我们可以使用`CWinApp`类来创建和管理...

    \嵌入式linux开发教程之进程与线程--千锋培训

    嵌入式Linux开发教程深入探讨了进程与线程的概念及其在操作系统中的实现,这对于理解和开发高效、并发的嵌入式应用程序至关重要。以下是这些知识点的详细解释: **一、基础知识:线程和进程** 1. **进程**:是操作...

    根据进程ID获取进程的内存使用量,CPU使用率,线程个数据信息

    在Windows中,`CreateToolhelp32Snapshot`和`Thread32First/Next`函数可以遍历系统所有线程并找到目标进程的线程数。在Linux中,`/proc/&lt;pid&gt;/task`目录下包含了进程的所有线程,子目录的数量就是线程个数。 了解了...

    线程,进程,程序的区别

    线程(Thread)有时被称为轻量级进程(Lightweight Process, LWP),它是程序执行流的最小单元。一个进程可以包含多个线程,这些线程共享进程中的资源,如内存和文件句柄。线程是被系统独立调度和分派的基本单位。...

Global site tag (gtag.js) - Google Analytics