前言
大家好,今天开始我们来聊一聊多线程。通过这样子的方式来督促自己对知识系统的学习,也希望能给大家一些帮助。
本篇主要知识点
- 什么是线程,什么是进程?
- 为什么要使用线程?
- 创建线程的方式,线程是如何启动的?
- 线程的常用方法
很多时候我都习惯使用脑图来记录问题,以及回答问题,这次也继续使用脑图来回答这些问题
网络异常,图片无法展示
|
下面我们开始稍微深入的探索一下线程的奥秘,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如何启动线程,我们可以跟着线程状态转换来跟踪一下源码。
网络异常,图片无法展示
|
- 第一个状态NEW
Thread thread = new Thread(() -> {}); System.out.println(thread.getState()); // 输出 NEW
- 第二个状态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的就是这个本地线程。