警告⚠️:本文耗时很长,先做好心理准备

需要jni知识才能理解本篇文章(扫盲链接:https://www.jianshu.com/p/87ce6f565d37)

java当中的线程和操作系统的线程是什么关系?
猜想: java thread —-对应-—> OS thread
Linux关于操作系统的线程控制源码:pthread_create()
Linux命令:man pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

根据man配置的信息可以得出pthread_create会创建一个线程,这个函数是linux系统的函数,可以用C或者C++直接调用,上面信息也告诉程序员这个函数在pthread.h, 这个函数有四个参数:

然后我们来在linux上启动一个线程的代码:

创建一个后缀名.c的文件:

//引入头文件
#include <pthread.h>
#include <stdio.h>
//定义一个变量,接受创建线程后的线程id
pthread_t pid;
//定义子线程的主体函数
void* thread_entity(void* arg)
{
while ()
{
usleep();
printf("i am new Thread!\n");
}
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
//调用操作系统的函数创建线程,注意四个参数
pthread_create(&pid,NULL,thread_entity,NULL);
//usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?为什么需要睡眠?如果不睡眠会出现什么情况
//让主线程睡眠,目的是为了让子线程执行
while ()
{
usleep();
printf("main\n");
}
}

运行命令:

gcc -o thread.out thread.c -pthread
Thread.out 是thread.c 编译成功之后的文件
运行:./thread.out 
输出:
i am new Thread!
main
i am new Thread!
main
i am new Thread!
main
i am new Thread!
main
。。。。。。
//一直交替执行

经过以上分析Linux线程创建的过程
可以试想一下java 的线程模型到底是什么情况?
分析: java代码里启动一个线程的代码:

import java.lang.Thread;
public class ThreadTest {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("i am new Thread!\n”)
}
};
thread.start();
}
}

这里启动的线程(start() 方法)和上面我们通过linux的pthread_create()函数启动的线程有什么关系呢?
只能去可以查看start()的源码了,看看java的start()到底干了什么事才能对比出来。

start源码
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
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 */
}
}
} //start0方法是一个native方法
//native方法:就是一个java调用非java代码的接口,该接口方法的实现由非java语言实现,比如C语言。
private native void start0();

根据Start()源码可以看到这个方法最核心的就是调用了一个start0方法,而start0方法又是一个native方法,故而如果要搞明白start0我们需要查看Hotspot的源码。
好吧那我们就来看一下Hotspot的源码吧,Hotspot的源码怎么看么??一般直接看openjdk的源码,openjdk的源码如何查看、编译调试?
Mac 10.14.4 编译openjdk1.9源码 及集成clion动态调试 : https://app.yinxiang.com/fx/b20706bb-ae55-4ec5-a17b-79930e7e67ea
我们做一个大胆的猜测,java级别的线程其实就是操作系统级别的线程,什么意思呢?
说白了我们大胆猜想 start()—>start0()—>ptherad_create()
我们鉴于这个猜想来模拟实现一下:
一:自己写一个start0()方法来调用一个 native 方法,在native方法中启动一个系统线程
//java 代码

public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start0();
}
//native方法
private native void start0();
}

二:然后我们来写一个c程序来启动本地线程:

#include <pthread.h>
#include <stdio.h>
//定义一个变量,接受创建线程后的线程id
pthread_t pid;
//定义子线程的主体函数
void* thread_entity(void* arg)
{
  while ()
  {
    usleep();
    printf("i am new Thread!\n");
  }
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
  //调用操作系统的函数创建线程,注意四个参数
  pthread_create(&pid,NULL,thread_entity,NULL);
  //usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?为什么需要睡眠?如果不睡眠会出现什么情况
  //让主线程睡眠,目的是为了让子线程执行
  while ()
  {
    usleep();
    printf("main\n");
  }
}

三:在Linux上编译运行C程序:

编译: gcc -o thread.out thread.c -pthread
运行: ./thread.out
就会出现线程交替执行:
main
i am new Thread!
main
i am new Thread!
main
i am new Thread!
main
。。。。。。

现在的问题就是我们如何通过start0()调用这个c程序,这里就要用到JNI了(JNI自行扫盲)
Java代码如下:

public class TestThread {
static {
//装载库,保证JVM在启动的时候就会装载,故而一般是也给static
System.loadLibrary("TestThread");
} public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start0();
  } private native void start0();
}
在Linux下编译成clas文件:
编译: javac java1.java
生成class文件:java1.class
在生成 .h 头文件:
编译: javah TestThread
生成class文件:TestThread.h
.h文件分析
#include <jni.h>
/* Header for class TestThread */
#ifndef _Included_TestThread
#define _Included_TestThread
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestThread
* Method: start0
* Signature: ()V
*/
//15行代码, Java_com_luban_concurrency_LubanThread_start0方法就是你需要在C程序中定义的方法
JNIEXPORT void JNICALL Java_TestThread_start0(JNIEnv *, jobject);
    #ifdef __cplusplus
}
#endif
#endif

然后继续修改.c程序,修改的时候参考.h文件,复制一份.c文件,取名threadNew.c 定义一个Java_com_luban_concurrency_LubanThread_start0 方法在方法中启动一个子线程:

#include <pthread.h>
#include <stdio.h>
//记得导入刚刚编译的那个.h文件
#include "TestThread.h"
//定义一个变量,接受创建线程后的线程id
pthread_t pid;
//定义子线程的主体函数
void* thread_entity(void* arg)
{
while ()
{
usleep();
printf("i am new Thread!\n");
}
}
//这个方法要参考.h文件的15行代码
JNIEXPOR void JNICALL Java_com_luban_concurrency_LubanThread_start0(JNIEnv *env, jobject c1){
  pthread_create(&pid,NULL,thread_entity,NULL);
  while()
  {
    usleep();
    printf("I am Java_com_luban_concurrency_LubanThread_start0 \n");
  }
}

解析类,把这个threadNew.c编译成为一个动态链接库,这样在java代码里会被laod到内存libTestThreadNative这个命名需要注意libxx,xx就等于你java那边写的字符串

gcc ‐fPIC ‐I ${JAVA_HOME}/include ‐I ${JAVA_HOME}/include/linux ‐shared ‐o libTestThreadNative.so threadNew.c
//需要把这个.so文件加入到path,这样java才能load到:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libLubanThreadNative.so}

直接测试,运行我们自己写的那个java类直接测试看看结果能不能启动线程:
运行:java java1
现象:
main
I am Java_com_luban_concurrency_LubanThread_start0
main
I am Java_com_luban_concurrency_LubanThread_start0
main
I am Java_com_luban_concurrency_LubanThread_start0
main
。。。。。。
我们已经通过自己写的一个类,启动了一个线程,但是这个线程函数体是不是java的是C程序的,这个java线程的run方法不同。接下来我们来实现一下这个run:(C来调用java的方法,是jni反调用java方法)
java的代码里面提供一个run方法:

public class TestThread {
static {
//装载库,保证JVM在启动的时候就会装载,故而一般是也给static
System.loadLibrary("TestThread");
} public static void main(String[] args) {
  TestThread testThread = new TestThread();
  testThread.start0();
} //这个run方法,要让C程序员调用到,就完美了
public void run(){
  System.out.println("I am java Thread !!");
} private native void start0();
}

C程序:

#include <pthread.h>
#include <stdio.h>
//记得导入刚刚编译的那个.h文件
#include "TestThread.h"
//定义一个变量,接受创建线程后的线程id
pthread_t pid;
//定义子线程的主体函数
void* thread_entity(void* arg)
{
  run();
}
//JNIEnv *env 相当于虚拟机
JNIEXPOR void JNICALL Java_com_luban_concurrency_LubanThread_start0(JNIEnv *env, jobject c1){
  //定一个class 对象
  jclass cls;
  jmethodID cid;
  jmethodID rid;
  //定一个对象
  jobject obj;
  jint ret = ;
  //通过虚拟机对象找到TestThread java class
  cls = (*env)->FindClass(env,"TestThread");
  if(cls == NULL){
    printf("FindClass Error!\n")
    return;
  }
  cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
  if (cid == NULL) {
    printf("GetMethodID Error!\n");
    return;
  }
  //实例化一个对象
  obj = (*env)->NewObject(env, cls, cid);
  if(obj == NULL){
    printf("NewObject Error!\n")
    return;
  }
  rid = (*env)->GetMethodID(env, cls, "run", "()V");
  ret = (*env)->CallIntMethod(env, obj, rid, Null);
  printf("Finsh call method!\n")
}
int main(){
  return ;
}
然后再走一遍生成.class、.h 、so 然后执行
jni反调用java编译:
gcc -o threadNew threadNew.c -I /usr/lib/jvm/java-1.8.-openjdk/include -I /usr/lib/jvm/java-1.8.-openjdk/include/linux -L /usr/lib/jvm/java-1.8.- openjdk/jre/lib/amd64/server -ljvm -pthread
指定:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libLubanThreadNative.so}
显示:
I am java Thread !!
Finsh call method!

至此c调用java的已经完成(只是模拟)(其实C调用java的时候并不是调用jni反射调用的,而是用的C++的一个函数)
由上可知java thread的调用及反调用:
调用了一个start0方法,而start0方法又是一个native方法,native方法是由Hotspot提供的,并且调用OS pthread_create()
证实: java thread —-对应-—> OS thread

原创不易,转载请标明出处

java并发笔记之java线程模型的更多相关文章

  1. java并发笔记之证明 synchronized锁 是否真实存在

    警告⚠️:本文耗时很长,先做好心理准备 证明:偏向锁.轻量级锁.重量级锁真实存在 由[java并发笔记之java线程模型]链接: https://www.cnblogs.com/yuhangwang/ ...

  2. Java学习笔记之---单例模型

    Java学习笔记之---单例模型 单例模型分为:饿汉式,懒汉式 (一)要点 1.某个类只能有一个实例 2.必须自行创建实例 3.必须自行向整个系统提供这个实例 (二)实现 1.只提供私有的构造方法 2 ...

  3. java并发笔记之四synchronized 锁的膨胀过程(锁的升级过程)深入剖析

    警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是锁的升 ...

  4. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  5. Java并发编程:Java的四种线程池的使用,以及自定义线程工厂

    目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...

  6. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  7. Java并发实战一:线程与线程安全

    从零开始创建一家公司 Java并发编程是Java的基础之一,为了能在实践中学习并发编程,我们跟着创建一家公司的旅途,一起来学习Java并发编程. 进程与线程 由于我们的目标是学习并发编程,所以我不会把 ...

  8. 【转载】Java系列笔记(3) - Java 内存区域和GC机制

    Java系列笔记(3) - Java 内存区域和GC机制 转载:原文地址http://www.cnblogs.com/zhguang/p/3257367.html 目录 Java垃圾回收概况 Java ...

  9. Java系列笔记(2) - Java RTTI和反射机制

    目录 前言 传统的RTTI 反射 反射的实现方式 反射的性能 反射与设计模式 前言 并不是所有的Class都能在编译时明确,因此在某些情况下需要在运行时再发现和确定类型信息(比如:基于构建编程,),这 ...

随机推荐

  1. 我所理解的Vue——学习心得体会1(Vue对象)

    初学Vue,总结如下: 1.首先要区分html的dom和js的dom 2.html的dom是View的范畴,js的dom是Model的范畴. 3.vue这库就是创建了伟大的new Vue()对象,把h ...

  2. 02-MySQL的安装和管理

    # mysql的安装和基本管理 # 01 数据库管理软件分类 ''' 分两大类: 关系型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql语句通用 ...

  3. SkyWorking基础:6.2版本安装部署

    就在今天,SkyWorking发布了6.2版本. 概述 什么是SkyWorking SkyWalking是观察性分析平台和应用性能管理系统. 提供分布式追踪.服务网格遥测分析.度量聚合和可视化一体化解 ...

  4. spring 5.x 系列第14篇 —— 整合RabbitMQ (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...

  5. 【docker学习一】CentOS7.5+Docker安装及使用「安装、查看、pull、创建、进入镜像」

    记录安装配置以及使用的过程,可能会有多处摘抄,已注明照抄地址,侵删. 是什么:个人理解,是一种移植性很强的虚拟机,支持版本控制(类似于git),同一个服务器可以运行多个docker容器,每个docke ...

  6. 【设计模式】行为型10中介者模式(Mediator Pattern)

    中介者模式(Mediator Pattern)     这里笔者完全参考了:http://www.runoob.com/design-pattern/mediator-pattern.html,案例精 ...

  7. 棋盘问题 POJ - 1321(dfs)

    #include<iostream> #include<cstdio> #include<cstring> using namespace std; int n, ...

  8. HDU 3062:Party(2-SAT入门)

    http://acm.hdu.edu.cn/showproblem.php?pid=3062 题意:中文. 思路:裸的2-SAT.判断二元组的两个人是否在同一个强连通分量. 学习地址:http://w ...

  9. [AI开发]目标跟踪之速度计算

    基于视频结构化的应用中,目标在经过跟踪算法后,会得到一个唯一标识和它对应的运动轨迹,利用这两个数据我们可以做一些后续工作:测速(交通类应用场景).计数(交通类应用场景.安防类应用场景)以及行为检测(交 ...

  10. Bzoj 3654 图样图森波 题解

    3654: 图样图森破 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 123  Solved: 66[Submit][Status][Discuss] ...