Java中的线程模型及线程生命周期

本文解释了线程生命周期的不同定义,重点关注Java和操作系统视角,并详细解析了wait(),notify(),join(),sleep(),stop(),interrupt()等函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

线程生命周期和生命周期函数是面试中的重灾区之一。但是聊到生命周期,在博客上能搜到有人说是5种,有人说是7种,甚至有6种的说法,导致理解这个原本不算复杂的问题变得模糊。本文和大家分享一下我自己的心得,希望能帮助大家清晰的理解线程生命周期,及为何有各种博客对线程生命周期有不同的说法

1.线程模型,线程生命周期

1) 理解线程

有一句定义我认为特别经典:进程是承担分配系统资源的基本实体,线程是独立调度的基本单位。

从java-web开发程序员的角度看,一个前端请求进来服务器;一个异步方法调用;一个MQ的监听接到消息,用完完成这些的,都是一个线程

2)线程生命周期模型

我们在各个博客看到的4,5,6,7种生命周期状态的,其实是不同角度去定义线程生命周期。作为java开发,我认为,我们只需要两个角度来理解线程生命周期

1>操作系统角度,线程的生命周期模型

线程被新建,新建后进入就绪队列变成就绪态,然后等到CPU分配资源,即抢占到了CPU时间片后可开始执行,变成运行态,运行态中,可能由于各种因素,如IO之类导致运行的线程变成阻塞态,阻塞态的线程通过被唤醒,再次成为就绪态。当全部代码执行完毕,线程死亡。

2>Java角度线程的生命周期模型

java种,为了更好的控制线程和锁,将线程状态细分为了6种,在Thread.Statu种有定义,大家可以点进Thread看一下

其状态流转及函数调用如下图:

注意,Thread定义的状态中,对于运行态和就绪态,统一放在了RUNABLE种,所以java的RUNABLE实际上对应了操作系统的两种状态,分别是:

就绪态,运行态。

运行态可以通过调用yeid()变成就绪态,就绪态无法手动变成运行态

3>操作系统的线程状态和JAVA的线程状态对应关系

2.生命周期相关函数 wait(),notify(),notifyAll(),join(),sleep(),stop(),interrupt()

·Object.yeid():

        暂时释放线程对CPU的占用,线程会由运行态从新回到就绪态再次等待分配到CPU资源

·Object.wait(),Object.notify(),Object.notifyAll():

        Object.wait()用于挂起线程,notify,notifyAll则用于唤醒。这三个函数都依赖于对象的监视器(Monitor),对Monitor没有概念的同学可以看一下我的另外一篇分享

        关于锁需要知道的知识-理论篇-优快云博客

        大致说一下minitor,每个对象 作为锁资源( 即synchronized(对象) ) 时候都会绑定一个Monitor对象,Monitor对象中有一个waitSet集合,可以粗略理解为下图结构:

        而在线程中调用了Object.wait()线程就会被加入到这个Object对应的Monitor的waitSet中并变成阻塞态。这也就是为什么,obj.waitI()必须在synchronized(obj) 中调用。因为obj在被synchronized锁定后,才会绑定Monitor对象,而obj.waitI(),要将当前线程放入Monitor对象的waitSet集合

·Object.notify(),Object.notifyAll()

        notify()是从waiSet中随机的取一个唤醒,而notifyAll()则是唤醒全部

·Thread.join()

先上一张代码图

图左侧代码中,主线程调用子线程对象join方法(注意,对象的!对象的!),这时,主线程会进入阻塞,等待子线程执行结束后继续执行。

        了解join()用法后,分析一下join()原理,图的右侧是java中对Thread的实现,可以看到join()的底层还是调用了Object.wait(),我们之前讲wait()时候说了,wait()一定是基于某个锁资源对象的,所以,thread.join()方法是一个synchronize的方法。即thread本身就是一个锁资源,主线程调用 thread.join时候,相当于是进行了如下的代码:

主线程这时进入了thread对象对应的Monitro对象的waiSet中变成了阻塞状态。

了解以上之后,再思考一个问题,wait()/notify()成对出现才能阻塞/唤醒线程,thread.join底层既然是wait(),那么wait()后并没有进行notify(),主线程是如何醒过来的?

这里涉及到一个JVM对线程处理的知识点: 当线程执行线程thread执行完毕之后,JVM会调用lock.notify_all(thread);唤醒持有threadA这个对象锁的线程

所以,在子线程执行完成之后,主线程重新被唤醒继续执行。

·Thread.sleep(x)

了解sleep之前需要了解一个操作系统概念,叫做调度器,顾名思义,调度器是用来调度操作系统资源的,其功能就包括分配cpu资源给线程,让线程执行及挂起

而sleep的底层,其实就是向调度器发送了信号,由调度器来让出当前线程的cpu资源,并在指定时间后重新分配。

·Thread.stop() Thread.interrupt(), Thread.isinterrupt()

Thread.interrupt() 不需要记的很复杂,只需要记住两件事

1. 它不真的中断线程执行,只是给线程打一个标记,这个标记可以用isinterrupt()来判断.

2.如果是一个阻塞的线程调用它,会直接抛出InterruptedException

Thread.stop() 会真的停止线程,注意,停止线程时候,如果线程的代码块进行了 try catch finally ,finally的代码会执行

再说一下所谓的优雅停止线程,目前官方推荐是使用 interrupt(),isinterrupt() 完成,伪代码如下:

while(true) {

.......

if(isinterrupt()) {

释放资源,处理线程后事

}

}

但是,实际工作中,我们需要进行中断的线程并不一定是能用一个循环体来控制的,可能是一长串代码,在执行很耗时的IO操作或者运算操作,这种情况下我们需要根据产品需求,比如界面有个对任务的终止操作之类的,立刻中断线程执行,并释放线程所占有的资源。这种情况下其实我们还是选择用 stop,然后再 finally代码块中完成资源的释放

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值