oid *)&JVM_GetNextThreadIdOffset}
};
在源码文件 https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/thread.cpp 可以看到启动线程依赖系统级方法os::start_thread(thread)
。
void Thread::start(Thread* 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 (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::cast(thread)->threadObj(),
JavaThreadStatus::RUNNABLE);
}
os::start_thread(thread);
}
在源码文件 https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/os.cpp 找到os::start_thread
方法,可以看到系统创建了线程,并且状态设置为RUNNABLE。
void os::start_thread(Thread* thread) {
OSThread* osthread = thread->osthread();
osthread->set_state(RUNNABLE);
pd_start_thread(thread);
}
Linux系统并没有把线程和进程区别对待,无论线程还是进程都是一个数据结构,用task_struct
结构体表示,唯一的区别是共享的数据区域不同。
struct task_struct {
// 进程状态
long state;
// 虚拟内存结构体
struct mm_struct *mm;
// 唯一进程号
pid_t pid;
// 指向父进程的指针
struct task_struct *parent;
// 子进程列表
struct list_head children;
// 存放文件系统信息的指针
struct fs_struct *fs;
// 进程/线程打开的文件指针
struct files_struct *files;
};
以上代码是 task_struct 的极少部分字段。mm_struct是进程的虚拟内存空间,files_struct是进程将要读写的文件。Linux系统将一切外设和磁盘文件都当做文件处理,files_struct代表所有的IO操作。
从上图可以看到,Linux创建进程和子进程会申请不同的内存空间,读写不同的文件;创建进程和进程下的线程,共享了内存空间,读写一样的文件。因此多线程应用程序要利用锁机制,避免在同一区域写入错乱数据的问题。
2.2 线程的生命周期
操作系统的线程生命周期可以分为五种状态。分别是:初始状态、可运行状态、运行状态、休眠状态和终止状态。JVM将线程等待状态细分成两种,一共六种状态。
- NEW:创建。
- RUNNABLE:运行中。
- BLOCKED:受阻塞并等待某个监视器锁。
- WAITING:无限期地等待。
- TIMED_WAITING:等待指定时间。
- TERMINATED:终止。
2.3 线程的优先级
操作系统调度线程有两种方式:
JVM的线程调度默认是抢占式调度,线程调度器按照优先级决定调度哪个线程来执行。线程优先级的范围是1~10,默认的优先级是5,10极最高。线程优先级高的不一定先执行,优先级低只是获得调度的概率低,并不是一定最后被调度。通过setPriority()
可以改变线程优先级。
2.4 JVM守护线程
守护线程是一种JVM中特殊的线程,在后台完成一些系统性的服务,比如垃圾回收。应用程序创建的线程叫做用户线程,完成具体的业务操作。程序中所有的用户线程执行完毕之后,不管守护线程是否结束,JVM都会自动结束。任何线程都可以通过setDaemon()
设置为守护线程和用户线程,如下代码所示:
public class DaemonThreadDemo {
public static void main(String[] args) {
System.out.println("--主线程开始--");
Thread thread = new Thread(() -> {
while (true) {
System.out.println("执行守护线程");
}
});
thread.setDaemon(true);
thread.start();
System.out.println("--主线程结束--");
}
}
程序运行结果:
--主线程开始--
--主线程结束--
执行守护线程
执行守护线程
执行守护线程
执行守护线程
执行守护线程
Process finished with exit code 0
当一个应用程序需要在后台持续做某件事情,就是守护线程的典型应用场景。比如开发一款社交软件,开启守护线程持续监听聊天消息。当应用程序退出时,守护线程一定会终止。
参考文章:https://www.codingbrick.com/archives/937.html