前言
一、什么是线程,什么是进程?
思考:
- 什么是线程,什么是进程?
- 为什么要使用线程?
- 创建线程的方式,线程是如何启动的?
- 线程的常用方法
很多时候我都习惯使用脑图来记录问题,以及回答问题,这次也继续使用脑图来回答这些问题
下面我们开始稍微深入的探索一下线程的奥秘,go!
二. Thread类构造方法
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);
}
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的主要实现
1 .本地方法
{"start0", "()V", (void *)&JVM_StartThread}
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);
}
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的就是这个本地线程。