前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程基础补充

线程基础补充

作者头像
cheese
发布2024-02-06 08:18:54
1000
发布2024-02-06 08:18:54
举报
文章被收录于专栏:Java PorterJava Porter

从 Start 一个线程说起

代码语言:javascript
复制
 public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
        }, "t1");
        t1.start();
    }
  • Thread.class 类中的源码
代码语言:javascript
复制
    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 */
            }
        }
    }

    private native void start0();
  • start 方法被 synchronized 进行修饰就是在创建一个线程的时候是必须全部走完的
  • started 标志位,表示线程是否已开启,true 为已开启,false 为未开启
  • start0 方法被 native 修饰,表示 jni 本地接口调用
    • 调用第三方 c 语言所编写的函数或者是操作系统的底层代码
  • 对于多线程与语言关系不大,是由底层操作系统所决定的

Java 线程理解以及 openjdk 中的实现

  • private native void start0();
  • Java 语言本身底层就是 C++语言
  • OpenJDK
    • openjdk\jdk\src\share\native\java\lang 目录下的 thread.c
      • Java 线程是通过 start 方法启动执行的,主要内容是在 native 方法 start0 中
      • openjdk 的写 JNI 一般是一一对应的,Thread.java 对应的就是 Thread.c
      • start0 其实就是 JVM_StartThread,此时查看源代码可以看到在 jvm.h 中找到声明,jvm.cpp 中有实现
代码语言:javascript
复制
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},
};
  • openjdk\hotspot\src\share\vm\prims 目录下的 jvm.cpp
代码语言:javascript
复制
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);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  Thread::start(native_thread); //开启一个线程
  • openjdk\hotspot\src\share\vm\runtime 目录下的 thread.cpp
代码语言:javascript
复制
void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    os::start_thread(thread); 
  }
}

Java 多线程相关概念

一把锁

  • synchronized

两个并

并发(concurrent)
  • 同一个实体上的多个事件
  • 是一台处理器上"同时"处理多个任务
  • 同一时刻,其实是只有一个事件在发生
并行(parallel)
  • 是在不同实体上的多个事件
  • 是在多台处理器上同事处理多个任务
  • 同一时刻,大家都在做自己的事情
并发 VS 并行

3 个程

  • 进程,系统中运行的一个应用程序,拥有自己的内存空间和系统资源
  • 线程,也被称为轻量级进程,在同一个进程中会有 1 或多个线程,是大多数操作系统进行时序调度的基本单元
  • 管程
    • 管程,monitor(监视器),就是常说的锁
代码语言:javascript
复制
  public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread t1 = new Thread(() -> {
            synchronized(o){
                System.out.println("此处的o就是一个管程对象");
            }
        }, "t1");
        t1.start();
    }

  • Monitor 其实是一种同步机制,他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码
  • 在 JVM’中同步是基于进入和退出监视器对象(Monitor ,管程对象)来实现的
  • 每个对象都会有一个 Monitor 对象,Monitor 对象会伴随 Java对象一同创建并销毁,底层由于 C++语言来实现

用户线程和守护线程

Java 线程分为用户线程和守护线程

  • 一般情况下不做特别说明配置,默认都是用户线程
  • 用户线程(User Thread)
    • 系统的工作线程,他会完成这个程序需要完成的业务操作
  • 守护线程(Daemon Thread)
    • 是一种特殊的为其他线程服务的,在后台默默完成一些系统性的服务
      • 比如垃圾回收线程
    • 作为一个服务线程,没有服务对象就没必要继续运行
      • 假如 用户线程全部结束,就意味着程序需要完成的业务操作已经结束了,系统可以退出
      • 假如系统中只剩下守护线程的时候,Java 虚拟机就会自动退回

线程的 daemon 属性

  • Thread 类中的源码,isDaemon 方法,用于判断当前线程是否为后台守护线程
image.png
image.png
  • 返回值,true 表示守护线程,false 表示用户线程
  • void setDaemon(boolean b) 方法,表示设置线程为守护线程,默认值为 false,默认为用户线程
    • 调用该方法传入 true 表示将当前线程设置为守护线程
    • 该方法执行须在线程对象调用 start()方法之前调用,否则会抛出IllegalThreadStateException异常
image.png
image.png

小 Demo 演示

  • 创建一个线程,判断线程类型,睡眠 3 秒后执行主线程
代码语言:javascript
复制
public static void main(String[] args){//一切方法运行的入口
    Thread t1 = new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + "\t 开始运行, " +
                           (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
        while (true) {
            //t1线程一直存活着
        }
    }, "t1");
    t1.start();

    //暂停几秒钟线程
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println(Thread.currentThread().getName() + "\t ----end 主线程");
}
  • 现象
image.png
image.png
  • 现象解释
    • t1 线程与 main 线程均为独立的用户线程各种独立存在
    • main 线程执行完打印输出后就结束了,而 t1 现在一直在执行 while(true){},因此线程一直存活

  • 将 t1 线程修改为守护线程,观察现象
代码语言:javascript
复制
        t1.setDaemon(true);
        t1.start();
image.png
image.png
  • 现象解释
    • t1 线程变为守护线程之后,系统中只存在 main 线程唯一一个用户线程
    • 当 main 线程执行完成之后,系统中就不存在用户线程了,以为着程序所要完成的业务操作已经结束,守护线程随 JVM 一起结束工作
  • void setDaemon(boolean)在 start()之后调用
代码语言:javascript
复制
        t1.start();
        t1.setDaemon(true);
image.png
image.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从 Start 一个线程说起
    • Java 线程理解以及 openjdk 中的实现
    • Java 多线程相关概念
      • 一把锁
        • 两个并
          • 并发(concurrent)
          • 并行(parallel)
          • 并发 VS 并行
        • 3 个程
        • 用户线程和守护线程
          • Java 线程分为用户线程和守护线程
            • 线程的 daemon 属性
              • 小 Demo 演示
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档