了解线程应该先清楚操作系统与JVM线程的联系与区别
前置了解:
内核空间主要操作访问CPU资源、I/O资源、内存资源等硬件资源,为上层应用程序提供最基本的基础资源,用户空间就是上层应用程序的固定活动空间,用户空间不可以直接访问资源,必须通过“系统调用”、“库函数”或“Shell脚本”来调用内核空间提供的资源。
线程的实现方式
用户级线程
程序员需要自己编写线程的结构体,以及创建销毁调度,操作系统只能感知到进程,可以实现并发,但是不能实现真正的并行
优势:不用内核态与用户态转换,减少资源消耗
不足:操作系统感知到的仍是以进程为单位的,如果其中一个线程进行了系统调用,则整个进程都会阻塞
内核级线程
运行在内核态的线程,直接由内核负责,只能由内核完成调度。比如java.lang.Thread就是对内核级线程的一层封装
也就是线程怎么调度内核都写好了系统调用,程序员只负责调用即可,不需要像用户态线程那样需要由用户自己编写运行销毁等方法。
但是内核级的线程切换必须在核心态下进行,上下文切换会消耗资源
混合模式
N个用户线程对应到M个内核态线程
多线程模型
由几个用户级线程映射到几个内核级线程的问题引出了多线程模型
- 一对一
- 多对一
- 多对多
JVM线程
在<<深入理解JAVA虚拟机>>中说明了:
在 JDK 1.2 之前,Java 线程是基于称为 "绿色线程"(Green Threads)的用户级线程实现的,也就是说程序员大佬们为 JVM 开发了自己的一套线程库或者说线程管理机制。
而在 JDK 1.2 及以后,JVM 选择了更加稳定且方便使用的操作系统原生的内核级线程,通过系统调用,将线程的调度交给了操作系统内核。而对于不同的操作系统来说,它们本身的设计思路基本上是完全不一样的,因此它们各自对于线程的设计也存在种种差异,所以 JVM 中明确声明了:虚拟机中的线程状态,不反应任何操作系统中的线程状态。
也就是说,在 JDK 1.2 及之后的版本中,Java 的线程很大程度上依赖于操作系统采用什么样的线程模型,这点在不同的平台上没有办法达成一致,JVM 规范中也并未限定 Java 线程需要使用哪种线程模型来实现,可能是一对一,也可能是多对多或多对一。
现今 Java 中线程的本质,其实就是操作系统中的线程,其线程库和线程模型很大程度上依赖于操作系统(宿主系统)的具体实现,在windows和linux中都是使用一对一的线程模型实现的。一条java线程映射到一条轻量级进程上
操作系统与JVM线程状态
操作系统线程状态
JVM状态
以上每个方格都代表着JAVA程序中创建线程的一个关键状态
比如NEW,即使用Thread thread = new Thread()创建了线程,但是并没有启动
备注:RUNNABLE状态可以理解为复合状态:操作系统中的就绪态+运行态。yield方法会使运行态变为就绪态。yield方法后续会详解
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println(thread.getName() + ":当前状态:" + thread.getState());
thread.start();
System.out.println(thread.getName() + ":当前状态:" + thread.getState());
Thread thread2 = new Thread(() -> {
synchronized (lock) {
}
});
thread2.start();
Thread.sleep(1000);
System.out.println(thread2.getName() + ":当前状态:" + thread2.getState());
Thread thread3 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread3.start();
System.out.println(thread.getName() + ":当前状态:" + thread.getState());
Thread.sleep(2000);
System.out.println(thread3.getName() + ":当前状态:" + thread3.getState());
// 给thread发送中止信号
thread.interrupt();
// thread收到信号后不一定会马上停止,等待两秒
Thread.sleep(2000);
System.out.println(thread.getName() + ":当前状态:" + thread.getState());
}
运行结果: Thread-0:当前状态:NEW Thread-0:当前状态:RUNNABLE Thread-1:当前状态:BLOCKED Thread-0:当前状态:TIMED_WAITING Thread-2:当前状态:WAITING Thread-0:当前状态:TERMINATED
接下篇线程基础---基础方法_求求大佬放过我的博客-CSDN博客线程启动在Thread类中注释标明有两种方式创建新的执行线程:一种是声明一个类是Thread的子类。这个子类应该重写类Thread的run方法。然后可以分配和启动子类的实例创建线程的另一种方法是声明一个实现Runnable接口的类。这个类然后实现run方法。然后可以分配类的实例,在创建Thread时作为参数传递,并启动两种方式对比:从java语法方面来看,java没有多继承,如果使用继承Thread的方式,就不能再继承其他类,限制了代码的可扩展性从效率方面来看,如果通过实现Runnabhttps://blog.csdn.net/weixin_40501674/article/details/124369638



