栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

守护线程和钩子

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

守护线程和钩子

什么是守护线程?

守护线程就是在所有用户线程结束之后,所有守护线程的终止,进而虚拟机进程就会退出。
呃, 这里为什么把用户线程加粗呢? 想一个问题,如果用户线程代表 除了守护线程之后的所有线程包括(我们自己创建的以及jvm里面c++代码创建的),守护线程会自己终止吗?

先不说代码(毕竟jvm 层的c++代码可太难了),就说一般的idea如果让你来设计 如何实现?

方式1: 所有守护线程都会循环有个全局的标志位判断,比如一个静态变量,在最后一个用户线程退出的时候 把找个标志位复制为true, 这个时候守护线程就执行return。

这种方式可取吗? 那也太麻烦了, 用户要么写守护线程的时候自己来判断这个标记位,或者使用动态代理的方式为runnable 织入横切逻辑 ,这也坑定不可取的

方式2: 线程是由操作系统提供的创建函数的(我想称为函数对于c 面向过程的语言, 像c++的类的函数,我想称为行为或者方法)。那么操作系统也应该会提供停止正在运行的线程的函数,比如我们只要把第一次调操作系统创建线程得到的tid线程id,传给操作系统的停止线程的函数就可以。

那要实现方式二的关键点在哪里: 要执行的关闭线程集合从哪里来? 谁来关闭这些线程集合? 基于这两个点可以考虑把 守护线程和用户线程的线程集合存在一个公共的存储能力比如 数组、链表等, 这里推荐链表, 好的, 那接着 谁来执行这个关系的行为呢? ok 那就可以来一个 专门的用户线程来最后执行 对守护线程的线程集合的关闭操作。

那么接着 问题又来了? 这个专门清楚 守护线程的用户线程我们称之为DestroyJavaVM 吧, 那这个线程的创建时机是什么时候?

我们知道 java程序 也就是一个class文件 一般如果要可执行都得又一个 mian方法。这个main方法是jvm在 底层内部启动一个线程 通过jni的方式来回调 java的main方法, 所以一般jvm中一个java程序运行都会有一个线程来执行main方法,所以这个main方法执行结束之后 可以创建一个DestroyJavaVM 线程来执行 销毁守护线程的逻辑。但是问题又来了,
如果这时候还有别的我们自己定义的用户线程呢?所以DestroyJavaVM 在执行销毁逻辑之前得对 用户线程集合进行判断,如果大于1(他自己和别人)就进行wait, 如果另一个用户线程退出的时候判断用户线程数为1那么就notify把DestroyJavaVM唤醒执行销毁逻辑进行。

动手验证一下:

通过idea的debug查询DestroyJavaVM线程

 在想一下, 这个时候 执行main方法的mian线程还没有结束,这个时候创建DestroyJavaVM 是没必要的,浪费资源,所以jvm就把DestroyJavaVM线程的创建留在了 main线程退出之后, 那么怎么验证呢? 我们需要new 一个thread.

 现在不就出现了。 哈哈哈。 但是注意没有 是 RUNNING状态, 按道理来说应该 是wait呀,,继续验证,我们别使用debug , 使用sleep和打印看看。

public class DaemonThreads {
    
    
    public static void printDestoryVMState(){
        try {
            TimeUnit.SECONDS.sleep(3);
            Thread thread = Thread.currentThread();
            ThreadGroup threadGroup = thread.getThreadGroup();
            int i = threadGroup.activeCount();
            Thread[] threads = new Thread[i];
            threadGroup.enumerate(threads);
            Arrays.stream(threads).filter(x-> x.getName().equals("DestroyJavaVM")).forEach(x-> System.out.println(x.getState()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                printDestoryVMState();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(6);
                printDestoryVMState();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("mian线程结束");
    }
}

呃。。。。说实话还是 打印出 RUNABLE, 不应该是 WAIT吗。

好吧 看 大概 看一个,盲猜一下 。c++ 代码。

在这里插入图片描述

呃 jvm代码注释 白嫖大佬,得, 别管我看得看不懂就是 猜, 这里是 jvm c程序的 main方法入口。
呃如果要看如果调用起来的请看 java.cass 文件的main方法如何调用起来
最后回调完java的mian方法之后,线程结束mian方法退出,就要启动DestroyJavaVM线程了。

int JNICALL
JavaMain(void * _args) //这个方法使用操作系统的线程, 用来回调java的main线程
{
    JavaMainArgs *args = (JavaMainArgs *)_args;
    int argc = args->argc;
    char **argv = args->argv;
    int mode = args->mode;
    char *what = args->what;
    InvocationFunctions ifn = args->ifn;

    JavaVM *vm = 0;
    JNIEnv *env = 0;
    jclass mainClass = NULL;
    jclass appClass = NULL; // actual application class being launched
    jmethodID mainID;
    jobjectArray mainArgs;
    int ret = 0;
    jlong start, end;

    RegisterThread();

    
    start = CounterGet();
    if (!InitializeJVM(&vm, &env, &ifn)) {   //初始化虚拟机,内部创建虚拟机并开启main线程
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }

    if (showSettings != NULL) {
        ShowSettings(env, showSettings);
        CHECK_EXCEPTION_LEAVE(1);
    }

    if (printVersion || showVersion) {
        PrintJavaVersion(env, showVersion);
        CHECK_EXCEPTION_LEAVE(0);
        if (printVersion) {
            LEAVE();
        }
    }

    
    if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
        PrintUsage(env, printXUsage);
        CHECK_EXCEPTION_LEAVE(1);
        LEAVE();
    }

    FreeKnownVMs();  

    if (JLI_IsTraceLauncher()) {
        end = CounterGet();
        JLI_TraceLauncher("%ld micro seconds to InitializeJVMn",
               (long)(jint)Counter2Micros(end-start));
    }

    
    if (JLI_IsTraceLauncher()){
        int i;
        printf("%s is '%s'n", launchModeNames[mode], what);
        printf("App's argc is %dn", argc);
        for (i=0; i < argc; i++) {
            printf("    argv[%2d] = '%s'n", i, argv[i]);
        }
    }

    ret = 1;

    
    mainClass = LoadMainClass(env, mode, what);   //加载main函数所在的类文件
    CHECK_EXCEPTION_NULL_LEAVE(mainClass);
    
    appClass = GetApplicationClass(env);//JavaFX没有MainClass而是通过ApplicationClass启动的,这里获取ApplicationClass
    NULL_CHECK_RETURN_VALUE(appClass, -1);
    
    PostJVMInit(env, appClass, vm);//将ApplicationClass作为应用名传给JavaFX本身,比如作为主菜单
    
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");//获取main方法的方法ID
    CHECK_EXCEPTION_NULL_LEAVE(mainID);

    
    mainArgs = CreateApplicationArgs(env, argv, argc);  //解析main方法的参数
    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);

    
    (*env)->CallStaticVoidMe thod(env, mainClass, mainID, mainArgs);  //执行main方法

    
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
    LEAVE();//main方法执行完毕,JVM退出,包含两步:(*vm)->DetachCurrentThread,让当前Main线程同启动线程断联,然后创建一个新的名为DestroyJavaVM的线程,让该线程等待所有的非后台进程退出,并在最后执行(*vm)->DestroyJavaVM方法。
}
bool Threads::destroy_vm() {
  JavaThread* thread = JavaThread::current();

#ifdef ASSERT
  _vm_complete = false;
#endif
  // Wait until we are the last non-daemon thread to execute  等待自己是最后一个非守护线程条件
  { MutexLocker nu(Threads_lock);
    while (Threads::number_of_non_daemon_threads() > 1 )  //非守护线程大于1,则一直等待(等到等于1的时候往下执行)
      // This wait should make safepoint checks, wait without a timeout,
      // and wait as a suspend-equivalent condition.
      //
      // Note: If the FlatProfiler is running and this thread is waiting
      // for another non-daemon thread to finish, then the FlatProfiler
      // is waiting for the external suspend request on this thread to
      // complete. wait_for_ext_suspend_completion() will eventually
      // timeout, but that takes time. Making this wait a suspend-
      // equivalent condition solves that timeout problem.
      //
      Threads_lock->wait(!Mutex::_no_safepoint_check_flag, 0,
                         Mutex::_as_suspend_equivalent_flag);  //这里阻塞的线程会被remove方法里的notify唤醒
  }

  // Hang forever on exit if we are reporting an error.
  if (ShowMessageBoxOnError && is_error_reported()) {
    os::infinite_sleep();
  }
  os::wait_for_keypress_at_exit();

  if (JDK_Version::is_jdk12x_version()) {
    // We are the last thread running, so check if finalizers should be run.
    // For 1.3 or later this is done in thread->invoke_shutdown_hooks()
    HandleMark rm(thread);
    Universe::run_finalizers_on_exit();
  } else {
    // run Java level shutdown hooks
    thread->invoke_shutdown_hooks();//钩子代码是在这里触发的(设置时间长点,守护线程可以再存活一会儿)
  }

  before_exit(thread);

  thread->exit(true);//destroyJavaVM线程退出,至此java中不能执行任何代码

  // Stop VM thread.
  {
    // 4945125 The vm thread comes to a safepoint during exit.
    // GC vm_operations can get caught at the safepoint, and the
    // heap is unparseable if they are caught. Grab the Heap_lock
    // to prevent this. The GC vm_operations will not be able to
    // queue until after the vm thread is dead.
    // After this point, we'll never emerge out of the safepoint before
    // the VM exits, so concurrent GC threads do not need to be explicitly
    // stopped; they remain inactive until the process exits.
    // Note: some concurrent G1 threads may be running during a safepoint,
    // but these will not be accessing the heap, just some G1-specific side
    // data structures that are not accessed by any other threads but them
    // after this point in a terminal safepoint.

    MutexLocker ml(Heap_lock);

    VMThread::wait_for_vm_thread_exit();
    assert(SafepointSynchronize::is_at_safepoint(), "VM thread should exit at Safepoint");
    VMThread::destroy();
  }

线程退出的执行:

void Threads::remove(JavaThread* p) {//移除线程
  // Extra scope needed for Thread_lock, so we can check
  // that we do not remove thread without safepoint code notice
  { MutexLocker ml(Threads_lock);

    assert(includes(p), "p must be present");

    JavaThread* current = _thread_list;
    JavaThread* prev    = NULL;

    while (current != p) { //线程以链表形式存在,判断当前线程是否要删除的线程,如果不是,后移,直到找到为止
      prev    = current;
      current = current->next();
    }

    if (prev) {//被删除线程是否有前驱节点,如果有,则直接摘除,让前驱节点指向被删除节点的next节点(跨过被删除节点)
      prev->set_next(current->next());
    } else { //如果没有前驱节点,说明被删除节点就是第一个节点,直接_thread_list指向它的next节点
      _thread_list = p->next();
    }
    _number_of_threads--; //线程数量减一
    oop threadObj = p->threadObj();
    bool daemon = true;
    if (threadObj == NULL || !java_lang_Thread::is_daemon(threadObj)) {
      _number_of_non_daemon_threads--;  //非守护线程数量减1
      daemon = false;

      // Only one thread left, do a notify on the Threads_lock so a thread waiting
      // on destroy_vm will wake up.
      if (number_of_non_daemon_threads() == 1)  //当非守护线程数量为1时,唤醒在destroy_vm方法等待的线程(其实最后那个非守护线程就是销毁线程)
        Threads_lock->notify_all();
    }
    ThreadService::remove_thread(p, daemon);  //移除掉线程

    // Make sure that safepoint code disregard this thread. This is needed since
    // the thread might mess around with locks after this point. This can cause it
    // to do callbacks into the safepoint code. However, the safepoint code is not aware
    // of this thread since it is removed from the queue.
    p->set_terminated_value();

    // Now, this thread is not visible to safepoint
    p->set_safepoint_visible(false);
    // once the thread becomes safepoint invisible, we can not use its per-thread
    // recorder. And Threads::do_threads() no longer walks this thread, so we have
    // to release its per-thread recorder here.
    MemTracker::thread_exiting(p);
  } // unlock Threads_lock

  // Since Events::log uses a lock, we grab it outside the Threads_lock
  Events::log(p, "Thread exited: " INTPTR_FORMAT, p);
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/997650.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号