基于 Hotspot 的 Java 线程源码分析(上)

简介: Java 线程其实是映射到操作系统的内核线程上的,所以 Java 线程基本上也就是操作系统在进行管理。在 Linux系统中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的多个线程之间是共享资源的。

基本概念


Java 线程其实是映射到操作系统的内核线程上的,所以 Java 线程基本上也就是操作系统在进行管理。在 Linux系统中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的多个线程之间是共享资源的。


简单说明: 本文基于 openjdk 1.8 进行


线程状态


每种线程状态的切换条件, 以及调用方法如下图所示 :


image.png


线程具有一下几种状态 Java 的线程状态在 Thread.State 枚举中定义代码如下


public enum State {
    //新创建,未启动
    NEW,
    //在jvm 中运行,也可能正在等待操作系统的其他资源
    RUNNABLE,
    //阻塞,并且正在等待监视器锁
    BLOCKED,
    //处于等待状态的线程,正在等待另一个线程执行特定的操作
    WAITING,
    //限期等待, 可以设置最大等待时间
    TIMED_WAITING,
    //结束
    TERMINATED;
}


线程创建


  1. 继承 Thread 类, 代码如下:


class PrimeThread extends Thread {
    long minPrime;
    PrimeThread(long minPrime) {
        this.minPrime = minPrime;
    }
    public void run() {
        // compute primes larger than minPrime
        . . .
        }
}
// 启动线程
PrimeThread p = new PrimeThread(143);
p.start();


  1. 实现 Runable 接口, 代码如下 (通常推荐使用这种方式):


class PrimeRun implements Runnable {
    long minPrime;
    PrimeRun(long minPrime) {
        this.minPrime = minPrime;
    }
    public void run() {
        // compute primes larger than minPrime
        . . .
        }
}
// 启动线程
PrimeRun p = new PrimeRun(143);
new Thread(p).start();


hotspot 源码


JNI 机制

JNI 是 Java Native Interface 的缩写,它提供了若干的 API 实现了Java和其他语言的通信(主要是C和C++)。


image.png


JNI的适用场景当我们有一些旧的库,已经使用C语言编写好了,如果要移植到Java上来,非常浪费时间,而JNI可以支持Java程序与C语言编写的库进行交互,这样就不必要进行移植了。或者是与硬件、操作系统进行交互、提高程序的性能等,都可以使用JNI。需要注意的一点是需要保证本地代码能工作在任何Java虚拟机环境。


JNI的副作用一旦使用JNI,Java程序将丢失了Java平台的两个优点:


  1. 程序不再跨平台,要想跨平台,必须在不同的系统环境下程序编译配置本地语言部分。


  1. 程序不再是绝对安全的,本地代码的使用不当可能会导致整个程序崩溃。一个通用规则是,调用本地方法应该集中在少数的几个类当中,这样就降低了Java和其他语言之间的耦合。


举个例子这块操作比较多,可以参考如下的资料


www.runoob.com/w3cnote/jni…


启动流程


启动流程如下


image.png


线程启动


Java 创建线程 Thread 实例之后,是通过 start 方法进行启动该线程,通知执行。在 start 方法的内部,调用的是 start0() 这个本地方法。我们可以从该方法为入口分析 JVM 对于 Thread 的底层实现。


public synchronized void start() {
    // 判断线程状态
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    // 添加到组
    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 */
        }
    }
}
private native void start0();


start0() 是一个本地方法,咱们按照 JNI 规范可以到 hotspot 虚拟源码中查找 java_lang_Thread_start0 这个函数。定义如下:


/*
 * Class:     java_lang_Thread
 * Method:    start0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_Thread_start0
  (JNIEnv *, jobject);


通过注释 Method:    start0 我可以猜到,在 jvm 的内部也可能会存在 start0 这个方法,于是我又搜索了一下这个方法,找到了 Thread.c 文件。可以看到里面有一个 Java_java_lang_Thread_registerNatives() 方法,这就是用来初始化在 Thread.java 与其他方法的绑定,并且在 Threa.java 的第一个 static 块中就调用了这个方法,保证这个方法在类加载中是第一个被调用的方法。这个 native 方法的作用是为其他 native 方法注册到JVM中。 代码如下所示:


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月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
196 1
|
2月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
216 1
|
3月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
168 0
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
274 16
|
4月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
4月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
295 0
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
172 6
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
311 83

热门文章

最新文章