【多线程】线程是如何启动的?

简介: 大家好,今天开始我们来聊一聊多线程。通过这样子的方式来督促自己对知识系统的学习,也希望能给大家一些帮助。

前言

大家好,今天开始我们来聊一聊多线程。通过这样子的方式来督促自己对知识系统的学习,也希望能给大家一些帮助。

本篇主要知识点

  1. 什么是线程,什么是进程?
  2. 为什么要使用线程?
  3. 创建线程的方式,线程是如何启动的?
  4. 线程的常用方法

很多时候我都习惯使用脑图来记录问题,以及回答问题,这次也继续使用脑图来回答这些问题

网络异常,图片无法展示
|

下面我们开始稍微深入的探索一下线程的奥秘,go!

Thread类构造方法

1.1 主要构造方法

//构造器
public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    init(group, target, name, stackSize);
}
//使用当前的AccessControlContext初始化线程。
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

主要方法 概念: ThreadGroup:线程组,指的这个现在在哪个组下 Runnable:指需要执行的任务 name:线程名称 stackSize:线程所需要的栈大小 acc:如果为null则调用AccessController.getContext inheritThreadLocals:如果为true,则从构造器中继承局部变量的初始值

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
                  Thread parent = currentThread();
                  ...
     //如果acc为空,则从AccessController.getContext()中获取
     //AccessController类用于访问控制的操作和决定。通过获取上下文的快照
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
       ...
     //如果acc为空,则从AccessController.getContext()中获取
     //AccessController类用于访问控制的操作和决定。通过获取上下文的快照
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
      ...
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
      ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
                  }

1.2 Thread常用方法

//获取当前线程
Thread currentThread()
//让出CPU
void yield();
//使当前线程睡眠一段时间
void sleep(long millis);
//使当前线程停止
void interrupt();
//等待这个线程执行结束
void join(long millis, int nanos);

线程状态转换

网络异常,图片无法展示
|

看完这,我们再来看下Thread如何启动线程,我们可以跟着线程状态转换来跟踪一下源码。

网络异常,图片无法展示
|

  1. 第一个状态NEW
Thread thread = new Thread(() -> {});
    System.out.println(thread.getState()); // 输出 NEW 
  1. 第二个状态RUNNABLE 他包括了RREAY和RUNNING,在进入RUNNABLE,那我们肯定需要调用Thread.start()。

线程调用start开始执行,虚拟机调用run()的方法。

public synchronized void start() {
     //主方法线程和系统组线程创建通过VM
     //状态值为0,设置状态为NEW
     //threadStatus 使用volatile是变量称为可见
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    //将线程加入分组,未启动计数减一
    group.add(this);
    boolean started = false;
    try {
        //{"start0","()V",(void *)&JVM_StartThread))}
        //虚拟机启动一个本地线程,本地线程的创建会调用当前系统创建线程的方法进行创建
        //线程执行时会回调run方法进行业务逻辑的处理
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                //线程启动失败
                group.threadStartFailed(this);
            }
        } 
    }
}

我们仔细看下start0的主要实现

2.1 本地方法

{"start0",           "()V",        (void *)&JVM_StartThread}

2.2 JNI把本地方法注册到jvm中

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  JavaCalls::call_virtual(&result,
                          obj,
                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}

2.3 作用是创造一个native_thread本地线程

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL; 
      ...
      native_thread = new JavaThread(&thread_entry, sz);
  ...
  Thread::start(native_thread);
JVM_END

总结

今天我们主要来了解了一下多线程的基础,主要是了解线程的含义,一个进程包含多个线程,一个线程执行一个任务,通过多线程我们来实现并发,以及线程的创建方式主要是Thread和Runnable,最后主要是线程的状态流转,由于篇幅过长,这篇我们先以探索start方法开个头,JNI把本地方法注册到jvm中,创建一个native_thread,真正执行run的就是这个本地线程。

相关文章
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
64 1
|
6天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
14天前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
78 10
spring多线程实现+合理设置最大线程数和核心线程数
|
22天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
40 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
5天前
|
NoSQL 网络协议 Unix
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
16 1
|
8天前
|
Python
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...
|
24天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
38 10
|
6天前
|
Java
COMATE插件实现使用线程池高级并发模型简化多线程编程
本文介绍了COMATE插件的使用,该插件通过线程池实现高级并发模型,简化了多线程编程的过程,并提供了生成结果和代码参考。
|
1月前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。