文章目录
从 Start 一个线程说起
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
}, "t1");
t1.start();
}
- Thread.class 类中的源码
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 中有实现
- openjdk\jdk\src\share\native\java\lang 目录下的 thread.c
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
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
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(监视器),就是常说的锁
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 方法,用于判断当前线程是否为后台守护线程
- 返回值,true 表示守护线程,false 表示用户线程
- void setDaemon(boolean b) 方法,表示设置线程为守护线程,默认值为 false,默认为用户线程
- 调用该方法传入 true 表示将当前线程设置为守护线程
- 该方法执行须在线程对象调用 start()方法之前调用,否则会抛出
IllegalThreadStateException
异常
小 Demo 演示
- 创建一个线程,判断线程类型,睡眠 3 秒后执行主线程
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 主线程");
}
- 现象
- 现象解释
- t1 线程与 main 线程均为独立的用户线程各种独立存在
- main 线程执行完打印输出后就结束了,而 t1 现在一直在执行 while(true){},因此线程一直存活
- 将 t1 线程修改为守护线程,观察现象
t1.setDaemon(true);
t1.start();
- 现象解释
- t1 线程变为守护线程之后,系统中只存在 main 线程唯一一个用户线程
- 当 main 线程执行完成之后,系统中就不存在用户线程了,以为着程序所要完成的业务操作已经结束,守护线程随 JVM 一起结束工作
- void setDaemon(boolean)在 start()之后调用
t1.start();
t1.setDaemon(true);