Java 线程的启动和停止原理

线程的启动和停止原理

线程的启动

总所周知,我们在Java程序中启动一个线程,需要调用该线程的start()方法来启动线程。

也就是我们通过Java中定义的start()方法来启动一个线程

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 */
            }
        }
    }

我们可以发现start()方法会调用start0()方法

private native void start0();

可以发现start0()方法是一个native方法。即-是一个本地方法。意味着和平台相关。

也就是说,通过Java代码,我们并不能搞清楚start()方法是如何启动线程的。

回到Thread类

private static native void registerNatives();
    static {
        registerNatives();
    }

在Thread类中定义了一个静态的native方法,很显然,registerNatives()方法是被首先调用的。

该类的本地方法被定义在文件Thread.c中,Thread.c定义了各个操作系统平台要用的关于线程的公共数据和操作

Thread.c文件

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},
};

#undef THD
#undef OBJ
#undef STE

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

从这段代码我们可以看出,start0()方法首先会被注册。调用start0()方法,实际会执行JVM_StartTHread方法。

通过下载hotspot源码,可以找到jvm.cpp这个文件

在 jvm.cpp 这个文件中使用 JVM_ENTRY 来定义 JVM_StartThread函数
在这个函数里面创建了一个真正和平台相关的本地线程
即 new JavaThread(&thread_entry, sz);
在 thread.cpp 文件中第1558行可以找到 JavaThread方法

在方法内部可以找到os::create_thread-即调用平台创建线程的方法来创建线程
线程的启动-会调用Thread.cpp文件中的Thread::start(Thread*thread)方法,


在该方法中有一个函数调用:os::start_thread(thread)调用平台启动线程的方法
最终会调用Thread.cpp文件中的JavaThread::run()方法

总结:Java通过start方法调用本地方法start0方法。start0方法通过Thread类中的registerNatives方法进行注册。Thread类的本地方法被定义在Thread.c文件中。start0方法被注册后调用,实际上是调用了JVM_StartThread方法。在hotspot源码中,可以找到jvm.cpp文件,在该文件中可以找到os::create_thread即调用平台创建线程的方法来创建线程。最终会调用JavaThread::run方法来启动run方法

线程的终止

首先stop方法在结束一个线程的时候,并不会保证线程资源的正常释放,可能会导致程序出现一些不确定的状态。

要优雅地去中断线程,我们可以去使用interrupt()方法

Thread thread = new Thread(() -> {
            //  拿到当前线程地状态   默认情况下 isInterrupted 返回 false  通过 thread.interrupt() 变成了true
            while (!Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("the int num :" + i);

        }, "interruptDemo");

        thread.start();
        TimeUnit.SECONDS.sleep(1);

        //  判断  interrupt()  方法加与不加地区别
        thread.interrupt();

线程的复位

我们可以使用 interrupt() 方法来中断线程,同时我们可以使用 interrupted()  方法来对线程进行复位。通过 isInterrupted() 方法来监控当前线程的状态。

此外,还可以通过抛出 InterruptedException异常的方式,对线程进行进行被动复位。

通过代码来比对差异

private void usual() throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("num:" + i);
        }, "interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        //  中断线程
        thread.interrupt();
        //   线程是否被中断成功
        System.out.println("线程是否被中断成功:"+thread.isInterrupted());
    }

    private void interruptedException() throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
            System.out.println("Num:" + i);
        }, "interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
        System.out.println(thread.isInterrupted());
    }

当我们调用 interrupt() 方法,通常情况下,线程收到中断请求。 isInterrupted() 方法返回值由false变为true。但是像给出的示例interruptedException()方法,线程收到中断请求并不会立即中断,我们通过抛出InterruptedException的方式来使线程跳出循环。进而继续执行run()方法。也就是说,我们可以在catch方法块中自己设计,来决定当前线程接下来的走向。即“被动复位”

线程终止的原理

类似地 interrupt() 方法调用了 native 方法 interrupt0()  通过jvm.cpp文件找到对 JVM_Interrupt的定义

该处直接调用了Thread::interrupt(thr)方法,在 Thread.cpp 文件中对该方法的定义如下

最终调用了 os::interrupt 方法,即调用了平台的 interrupt 方法。方法的实现在os_*.cpp文件中,其中星号代表的是不同平台,因为 jvm 是跨平台的,所以对于不同的操作平台,线程的调度方式都是不一样的。

以os_linux.cpp文件为例

set_interrupted(true)实际上就是调用 osThread.hpp 中的set_interrupted()方法,在 osThread 中定义了一个成员属
性 volatile jint _interrupted;

通过代码分析,thread.interrupt()方法实际就是设置一个 interrupted 状态标识为 true、并且通过
ParkEvent 的 unpark 方法来唤醒线程。

1. 对于 synchronized 阻塞的线程,被唤醒以后会继续尝试获取锁,如果失败仍然可能被 park
       2. 在调用 ParkEvent 的 park 方法之前,会先判断线程的中断状态,如果为 true,会清除当前线程的中断标识
       3. Object.wait 、 Thread.sleep 、 Thread.join 会 抛 出InterruptedException

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值