进程:指在一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程。进程也是程序的执行过程,是系统运行程序的基本单位。系统运行一个程序就是一个进程从创建到运行到死亡的过程。
线程:进程内部的一个独立执行单元;一个进程可以并发运行多个线程。
进程与线程的区别:
进程:有独立的内存空间,进程中的数据存放空间(堆空间栈空间是独立的),至少包含一个线程
线程:堆空间共享,栈空间是独立的,线程消耗的资源比进程小得多
注意:
因为一个进程中多个线程是并发运行的,那么从微观角度看也是有先后顺序的,那个线程先调用完全取决于CPU的调度,程序员是干涉不了的,这也是多线程随机的原因
多线程的优势:
-
进程之间不能共享内存,而线程之间可以共享内存(堆)
-
系统创建进程需要为该进程重新分配系统资源,创建线程则代价小得多
-
Java本身内置多线程功能的支持
线程的创建方式:
1.继承Thread类重写run方法
2.实现Runnable接口实现run方法 调用时Thread t = new Thread(new 新类())
3.使用匿名内部类创建线程
Thread t = new Thread(){
@Override
public void run() {
System.out.println("xxx");
}
};
4.使用Lambda简化匿名内部类的写法
Thread t = new Thread(() -> System.out.println("xxx"));
启动线程是start不是run
原因:
Run方法:在本地线程内调用run方法,和其他方法调用没区别,可以重复多次调用
Statr方法:启动一个线程,实际上还是调用这个run方法
从源码分析为什么使用start开启线程
可以看到start方法中调用了start0方法,而start0方法并没有具体的方法体,而有native关键字修饰,简单来讲native method就是一个java调用非java的接口,即该方法的实现由非java语言实现。
而在Thread类的开头我们可以看到
有个本地静态方法registerNatives,而在静态块中调用了这个方法,相当于当这个类加载到JVM的时候它就会被调用,进而注册相应的本地方法
查看OpenJDK的源码
http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/00cd9dc3c2b5/src/share/native/java/lang/Thread.c
可以看到最后调用了JVM_StartThread方法,通过名字大概可以猜测是JVM启动线程的一个方法
在 jvm.cpp 中,有如下代码段:
1 2 3 4 5 6 7 8 9 | 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); } |
|
|
这里JVM_ENTRY是一个宏,用来定义JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程,其线程函数是 thread_entry
1 2 3 4 5 6 7 8 9 | 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 用宏定义的:
1 2 3 4 5 | class vmSymbolHandles: AllStatic { … template(run_method_name,"run") … } |
|
|
至于 run_method_name 是如何声明定义的,因为涉及到很繁琐的代码细节,本文不做赘述。感兴趣的读者可以自行查看 JVM 的源代码
Java 线程创建调用关系图
综上所述,Java 线程的创建调用过程如 图 1 所示,首先 , Java 线程的 start 方法会创建一个本地线程(通过调用 JVM_StartThread),该线程的线程函数是定义在 jvm.cpp 中的 thread_entry,由其再进一步调用 run 方法。可以看到 Java 线程的 run 方法和普通方法其实没有本质区别,直接调用 run 方法不会报错,但是却是在当前线程执行,而不会创建一个新的线程
参考文档:
https://www.ibm.com/developerworks/cn/java/j-lo-processthread/#icomments