解决问题:java当中的线程和操作系统的线程是什么关系?
关于操作系统的线程
1.linux操作系统的线程控制原语
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
2.linux内核函数之pthread_create
根据man配置的信息:
参数 | 解释 | 备注 |
---|---|---|
pthread_t *thread | 传出参数,调用之后会传出被创建线程的id | 定义pthread_t pid;继而取地址 &pid |
const pthread_attr_t *attr | 线程属性,关于线程属性是Linux的知识 | 在学习pthread_create函数的时候一般传NULL,保持默认属性 |
void (start_routine)(void *) | 线程的启动后的主体函数相当于java当中的run | 需要你定义一个函数,然后传函数名即可 |
void *arg | 主体函数的参数 | 如果没有可以传NULL |
3.创建一个os线程
实验步骤;
1.在opt/local/cCode/thread 目录下
2.新增一个thread.c文件 cat>>thread.c
#include <pthread.h>// //导入对应的头文件
#include <stdio.h>
pthread_t pid;//定义一个变量,接受创建线程后的线程id
//定义线程的主体函数
void* thread_entity(void* arg)
{
while(1)
{ //线程睡眠
usleep(100);
printf("i am new Thread!");
}
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
//调用操作系统的函数创建线程,注意四个参数
pthread_create(&pid,NULL,thread_entity,NULL);
//usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?
//为什么需要睡眠?如果不睡眠会出现什么情况
while(1)
{
usleep(100);
printf("main
");
}
return 0;
}
4.gcc -o xx thread.c -pthread 编译c文件
ps: 编译的时候报错 如下所示:
报错解决:增加头文件 unistd.h http://www.voidcn.com/article/p-nyrseuhh-da.html https://stackoverflow.com/questions/29370813/implicit-declaration-of-function-usleep
修改过后的代码
#include <pthread.h>//头文件
#include <stdio.h>
#include< unistd.h>
pthread_t pid;//定义一个变量,接受创建线程后的线程id
//定义线程的主体函数
void* thread_entity(void* arg)
{
while(1)
{
usleep(100);
printf("i am new Thread!");
}
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
//调用操作系统的函数创建线程,注意四个参数
pthread_create(&pid,NULL,thread_entity,NULL);
//usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?
//为什么需要睡眠?如果不睡眠会出现什么情况
while(1)
{
usleep(100);
printf("main
");
}
return 0;
}
5.gcc -o xx thread.c -pthread 编译成功
6.执行 ./xx
./xx
输出结果:main线程跟 使用os函数pthread_create 创建的线程交替执行
关于Java线程
假设有了上面知识的铺垫,那么可以试想一下java的线程模型到底是什么情况呢?
在java代码里启动一个线程的代码
public class Example4Start {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("i am new Thread!");
}
};
thread.start();
}
}
问题:这里启动的线程和上面我们通过linux的pthread_create函数启动的线程有什么关系呢?
只能去可以查看start()的源码了, 看看java的start()到底干了什么事才能对比出来.
start源码的部分截图,可以看到这个方法最核心的就是调用了一个start0方法,而start0方法又是一个native方法,
即:start()->start0 [native方法】
故而如果要搞明白start0我们需要查看Hotspot的源码,好吧那我们就来看一下Hotspot的源码吧,Hotspot的源码怎么看么?
一般直接看openjdk的源码,openjdk的源码如何查看、编译调试?openjdk的编译我们后面会讨论,
在没有openjdk的情况下,我们做一个大胆的猜测:
java级别的线程其实就是操作系统级别的线程,什么意思呢?说白了我们大胆猜想:
java 中start方法-》start0本地方法-》调用os库函数:ptherad_create创建线程
我们鉴于这个猜想来模拟实现一下。
验证猜想demo:
1.LubanThread.java
public class LubanThread {
public static void main(String[] args) {
LubanThread lubanThread =new LubanThread();
lubanThread.start0();
}
private native void start0();
}
这里我们让自己写的start0调用一个本地方法,在本地方法里面去启动一个系统线程,好吧我们写一个c程序来启动本地线程
#include <pthread.h>
#include <stdio.h>
#include< unistd.h>
pthread_t pid;
void* thread_entity(void* arg)
{
while(1){
usleep(100);
printf("I am new Thread
");
}
}
void* start(){
pthread_create(&pid,NULL,thread_entity,NULL);
while(1){
usleep(100);
printf("I am main
");
}
}
int main()
{
start();
return 0;
}
在linux上编译、运行上述C程序
gcc thread.c -o thread.out -pthread
./thread.out
结果是两个线程一直在交替执行,得到我们预期的结果。
现在的问题就是我们如何通过start0调用这个c程序,这里就要用到JNI了,(后面不jni吧。我不知道jni是啥?)
1.修改LubanThread .java文件
LubanThread .java
public class LubanThread {
static {
System.loadLibrary( "LubanThreadNative" );
}
public static void main(String[] args) {
LubanThread lubanThread =new LubanThread();
lubanThread.start0();
}
private native void start0(); // 调用上面c的start();
}
2.编译LubanThread.java 生产成 LubanThread.class字节码文件
javac LubanThread.java
3.生成LubanThread.h 头文件
javah LubanThread
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class LubanThread */
#ifndef _Included_LubanThread
#define _Included_LubanThread
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: LubanThread
* Method: start0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_LubanThread_start0
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
其中JNIEXPORT void JNICALL Java_LubanThread_start0
(JNIEnv *, jobject);方法就是你需要在C程序中定义的方法。
4.复制一份thread.c 命名为threadnew.c
注意: 引入头文件LubanThread.h
#include <pthread.h>//头文件
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h" //注意引入头文件
pthread_t pid;
void* thread_entity(void* arg)
{
while(1){
usleep(100);
printf("I am new Thread
");
}
}
/*
* Class: com_luban_concurrency_LubanThread
* Method: start0
* Signature: ()V
*/
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0
(JNIEnv * env, jobject c1){
pthread_create(&pid,NULL,thread_entity,NULL);
while(1){
usleep(100);
printf("I am main
");
}
}
int main()
{
// start();
return 0;
}
5.生成libLubanThreadNative.so 文件
gcc -fPIC -I /usr/lib/jdk/jdk1.8.0_162/include -I /usr/lib/jdk/jdk1.8.0_162/include/linux -shared -o libLubanThreadNative.so threadnew.c
解析类,把这个threadnew.c编译成为一个动态链接库,这样在java代码里会被laod到内存 libLubanThreadNative这个命名需要注意libxx,xx就等于你java那边写的字符串
注意:/usr/lib/jvm/java-1.8.0-openjdk/include需要修改成linux机器上jdk的位置:
使用:echo $JAVA_HOME 查看jdk安装位置 : 我的是 /usr/lib/jdk/jdk1.8.0_162
6.libLubanThreadNative.so 文件加入到path
做完这一系列事情之后需要把这个.so文件加入到path,这样java才能load到
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libLubanThreadNative.so}所在的路径
{libLubanThreadNative.so} 我的是 export LD_LIBRARY_PATH=/opt/local/cCode/thread
export LD_LIBRARY_PATH=/opt/local/cCode/thread
7.执行 java LubanThread
万事俱备,直接测试,运行我们自己写的那个java类直接测试看看结果能不能启动线程
打印结果:
牛逼!我们已经通过自己写的一个类,启动了一个线程,但是这个线程函数体是不是java的是C程序的,这个java线程的run方法不同。接下来我们来实现一下这个run方法 (C来调用java的方法,是jni反调用java方法)
猜想demo2:回调Java层的run方法
1.LubanThread.java新增run方法
public class LubanThread {
static {
System.loadLibrary( "LubanThreadNative" );
}
public static void main(String[] args) {
LubanThread lubanThread =new LubanThread();
lubanThread.start0();
}
//这个run方法,要让C程序员调用到,就完美了
public void run(){
System.out.println("I am java Thread !!");
}
private native void start0();
}
让我们思考一下现在的问题是什么?
就是让C程序当中的thread_entity方法调用到java当中的run方法,思路同样是jni反调用java方法
2.修改threadnew.c
现在我们只需要将原来threadnew.c中代码修改,利用JNI反向调用LubanThread类中run方法即可模拟出Java的线程了。当我打开threadnew.c的文件的时候,我傻了,怎么在thread_entity获取到JNIEnv对象?
于是开始我的尝试:
尝试一:利用全局变量将其传出去,很可惜失败了。
下面是错误的代码1
#include <pthread.h>//头文件
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h" //注意引入头文件
pthread_t pid;
JNIEnv * env;
void* thread_entity(void* arg)
{
//定一个class 对象
jclass cls;
jmethodID cid;
jmethodID rid;
//定一个对象
jobject obj;
jint ret = 0;
//通过虚拟机对象找到TestThread java class
cls = (*env)->FindClass(env,"TestThread");
if(cls == NULL){
printf("FindClass Error!
")
return;
}
//找到LubanThread类中无参的构造函数
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
printf("GetMethodID Error!
");
return;
}
//实例化一个对象
obj = (*env)->NewObject(env, cls, cid);
if(obj == NULL){
printf("NewObject Error!
")
return;
}
rid = (*env)->GetMethodID(env, cls, "run", "()V");
//ret = (*env)->CallIntMethod(env, obj, rid, Null);
// printf("Finsh call method!
")
while(1){
usleep(100);
(*env)->CallIntMethod(env, obj, rid, Null);
}
}
/*
* Class: com_luban_concurrency_LubanThread
* Method: start0
* Signature: ()V
*/
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv * envself, jobject c1){
env = envself;
pthread_create(&pid,NULL,thread_entity,NULL);
while(1){
usleep(100);
printf("I am main
");
}
}
int main()
{
// start();
return 0;
}
尝试二:利用搜索引擎查找到了c语言中嵌套Java虚拟机,很可惜还是失败了,
错误的代码2:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
void* thread_entity(void* arg){
int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
/*设置初始化参数*/
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni"; //用于跟踪运行时的信息
/*版本号设置不能漏*/
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
jclass cls;
jobject obj;
jmethodID cid;
jmethodID rid;
jint ret = 0;
//通过虚拟机找到Java的类(LubanThread)
cls = (*env)->FindClass(env, "LubanThread");
if (cls == NULL) {
printf("FindClass error!
");
}
//找到LubanThread类中无参的构造函数
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
printf("Query constructor error!
");
}
//实例化对象
obj = (*env)->NewObject(env, cls, cid);
if (obj == NULL) {
printf("NewObject error!
");
}
//查找run方法
rid = (*env)->GetMethodID(env, cls, "run", "()V");
if (rid == NULL) {
printf("Query runMethod error
");
}
while (1) {
usleep(100);
//调用run方法
ret = (*env)->CallIntMethod(env, obj, rid, NULL);
}
}
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *env, jobject c1){
pthread_create(&pid,NULL,thread_entity,NULL);
while(1){
usleep(100);
printf("I am main
");
}
}
int main(){
return 0;
}
尝试三:搜出来说我路径写错了,于是又修改了一遍路径,进行尝试,很可惜还是失败了,下面是错误的代码
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
void* thread_entity(void* arg){
int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
/*设置初始化参数*/
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=/usr/lib/jdk/jdk1.8.0_162/";
options[2].optionString = "-verbose:jni"; //用于跟踪运行时的信息
/*版本号设置不能漏*/
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
jclass cls;
jobject obj;
jmethodID cid;
jmethodID rid;
jint ret = 0;
//通过虚拟机找到Java的类(LubanThread)
cls = (*env)->FindClass(env, "LubanThread");
if (cls == NULL) {
printf("FindClass error!
");
}
//找到LubanThread类中无参的构造函数
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
printf("Query constructor error!
");
}
//实例化对象
obj = (*env)->NewObject(env, cls, cid);
if (obj == NULL) {
printf("NewObject error!
");
}
//查找run方法
rid = (*env)->GetMethodID(env, cls, "run", "()V");
if (rid == NULL) {
printf("Query runMethod error
");
}
while (1) {
usleep(100);
//调用run方法
ret = (*env)->CallIntMethod(env, obj, rid, NULL);
}
}
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *env, jobject c1){
pthread_create(&pid,NULL,thread_entity,NULL);
while(1){
usleep(100);
printf("I am main
");
}
}
int main(){
return 0;
}
尝试四:我突然想到了安卓底层硬件要调用Java的函数,只能在c语言中调用,
它是怎么做的呢?于是搜了一下,最后仿照示例写了一段代码,终于成功了。代码如下
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
JavaVM *javaVM;
void *thread_entity(void *arg) {
JNIEnv* env = NULL;
//从jvm对象中取得JNIEvn对象
(*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
jclass cls;
jobject obj;
jmethodID cid;
jmethodID rid;
jint ret = 0;
//通过虚拟机找到Java的类(LubanThread)
cls = (*env)->FindClass(env, "LubanThread");
if (cls == NULL) {
printf("FindClass error!
");
}
//找到LubanThread类中无参的构造函数
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
printf("Query constructor error!
");
}
//实例化对象
obj = (*env)->NewObject(env, cls, cid);
if (obj == NULL) {
printf("NewObject error!
");
}
//查找run方法
rid = (*env)->GetMethodID(env, cls, "run", "()V");
if (rid == NULL) {
printf("Query runMethod error
");
}
while (1) {
usleep(100);
//调用run方法
ret = (*env)->CallIntMethod(env, obj, rid, NULL);
}
}
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *oldEnv, jobject c1) {
pthread_create(&pid, NULL, thread_entity, NULL);
while (1) {
usleep(100);
printf("I am main
");
}
}
//jvm启动的时候调用,将jvm对象返回出去
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
javaVM = vm;c
return JNI_VERSION_1_8;
}
int main() {
return 0;
}
3.然后再走一遍生成.class、.h 、so
gcc -fPIC -I /usr/lib/jdk/jdk1.8.0_162/include -I /usr/lib/jdk/jdk1.8.0_162/include/linux -shared -o libLubanThreadNative.so threadnew.c
此时报错:
```shell
LubanThread.h:2:10: fatal error: jni.h: No such file or directory
2 | #include <jni.h>
| ^~~~~~~
compilation terminated.
```
LubanThread.h:2:10: fatal error: jni.h: No such file or directory
2 | #include <jni.h>
| ^~~~~~~
compilation terminated.
原因:
好像是#include "jni_md.h"会将文件包含在与jni.h相同的目录中,但是现在找不到了。
解决办法
在以前的JDK版本中,jni_md.h 似乎和另一个文件都位于include / linux文件夹中,但是include中都有指向这两个文件的符号链接。
于是,先在本地系统查找jni_md.h文件的位置,使用如下命令:
find / -name jni_md.h 2> /dev/null
在我的系统里,找到jni_md.h的路径为:
/usr/lib/jvm/jdk1.8.0_221/include/linux/jni_md.h
果然在include/linux文件夹下,因此,可以仅创建如下两个文件的符号链接:
$ sudo ln -s /usr/lib/jvm/jdk1.8.0_221/include/linux/jni_md.h /usr/lib/jvm/jdk1.8.0_221/include/jni_md.h
$ sudo ln -s /usr/lib/jvm/jdk1.8.0_221/include/linux/jawt_md.h /usr/lib/jvm/jdk1.8.0_221/include/jawt_md.h
最后顺利解决该问题!
https://www.cnblogs.com/geekHao/p/12817565.html
继续执行:
gcc -fPIC -I /usr/lib/jdk/jdk1.8.0_162/include -I /usr/lib/jdk/jdk1.8.0_162/include/linux -shared -o libLubanThreadNative.so threadnew.c
此时会有warnning,不用管:
root@ubuntu:/opt/local/cCode/thread2# gcc -o threadnnew threadnew.c -I /usr/lib/jdk/jdk1.8.0_162/include -I /usr/lib/jdk/jdk1.8.0_162/includ/linux -L /usr/lib/jdk/jdk1.8.0_162/jre/lib/amd64/server -ljvm -pthread
threadnew.c: In function ‘thread_entity’:
threadnew.c:10:43: warning: passing argument 2 of ‘(*javaVM)->AttachCurrentThread’ from incompatible pointer type [-Wincompatible-pointer-types]
10 | (*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
| ^~~~
| |
| const struct JNINativeInterface_ ***
threadnew.c:10:43: note: expected ‘void **’ but argument is of type ‘const struct JNINativeInterface_ ***’
root@ubuntu:/opt/local/cCode/thread2#
4.libLubanThreadNative.so 文件加入到path
export LD_LIBRARY_PATH=/opt/local/cCode/thread2
5.执行 java LubanThread
最后执行,发现已经成功了,可以回调我们Java类中run方法了,至此我们模拟的Java中Thread类已经完成了。
文件目录
TOdo:
todo1:未编译openjdk 源码,尝试失败。搞了5、6个钟头未成功
未干完的事情,本来想要按照教程看一下,检验一下面的步骤:
1.我们先写一个Java多线程程序,然后将写好的Java程序放到我们的编译好的JDK源码下面build/linux-x86_64-server-fastdebug/jdk/bin/路径下,可能每个人编译路径路径不一样,你要找到你对应的文件下面,不然调试不行。
2.但是苦于我搞了一上午源码编译未成功,也就是build/linux-x86_64-server-fastdebug/jdk/ 下面无法生成bin路径
所以保留下了代码有机会再尝试 ,(应该是没有机会);
Test.java
public class Test{
public static void main(String[] args){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("haha");
}
});
thread.start();
}
}
执行行javac命令将Test.java文件编译成Test.class文件,
最后我们在Clion中进行调试。调试之前,要进行对应的调试的配置。
当断点和调试配置弄好过后,我们可以开始debug了,来验证我们的猜想。
很高兴,我们的猜想是正确的。所以Java线程的创建会调用系统中的线程创建函数。
todo2:暂未去理顺这个线程的创建和启动流程源码分析
思考:线程执行为什么不能直接调用run()方法,而要调用start()方法?
这边不需要懂:
我大概记一下代码的位置有空回来复习:
1.D:shareUbuntu5+openjdk8openjdkjdksrcshare ativejavalangThread.c
#include "jni.h"
#include "jvm.h"
#include "java_lang_Thread.h"
#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"
#define STR "Ljava/lang/String;"
#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))
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},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
#undef THD
#undef OBJ
#undef STE
#undef STR
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
2.D:shareUbuntu5+openjdk8openjdkhotspotsrcsharevmprimsjvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
3.D:shareUbuntu5+openjdk8openjdkhotspotsrcoslinuxvmos_linux.cpp
pthread_create() 生成os的线程
总结:
java中线程和操作系统中线程是一一对应的,当调用线程的start方法,会调用线程中本地方法start0,然后会调用Thread.c中JVM_StartThread,然后会调用jvm.cpp中对应的方法,再然后会调用操作系统中pthread_create函数来创建线程,pthread_create创建好线程回调java类中run方法。
原文链接:https://blog.csdn.net/qq_36434742/article/details/106682656