[疯狂Java]多线程:线程的生命周期

1. 线程生命周期中的5大状态:

    1) 线程并非一启动(start)就立即进入运行状态,即使运行了也不会一直占用CPU资源,而是个线程之间不停轮换执行;

    2) 线程5大状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Stop);

    3) 其中可分为两类,一类是静态(新建、死亡),一类是动态(就绪、运行、阻塞);


2. 新建(New):

    1) 即new出来一个Thread对象后的即时状态;

    2) 此时线程仅仅是在虚拟机内存中分配了一段空间并初始化了其数据成员,并没有表现出任何动态特征,就跟普通的类对象(String等)被new出来时的状态一样;

    

3. 就绪(Runnable):

    1) 即调用start之后的即时状态;

    2) 此时线程已经准备就绪,即虚拟机已经为其转备好函数栈和程序计数器,但并未运行,只是在等待虚拟机的线程调度程序调度它;

    3) 具体什么进入CPU运行不由编程决定,而是由后台的线程管理器决定的;

    4) 注意:不能调用run来执行线程,必须调用start

         i. 因为线程改变状态的背后是信号传递的过程;

         ii. 调用start就表示线程对象向虚拟机线程调度器发送了一个准备就绪的信号,告诉虚拟机我可以等待运行了;

         iii. 虚拟机接受到信号后就会让线程调度程序关注它并进行相应的调度;

         iv. 如果直接调用run方法,因为run是普通的方法,里面并没有什么发送信号的代码,因此就会被当做普通方法直接执行而并不会被调度器调度而并发执行;

         v. 因此如果直接调用run就会被当做普通发发直接顺序执行而不会被当做线程执行体被并发执行;

!!还有一点要注意,如果直接手动调用run,那么就不能在run中调用成员方法getName来获得线程的名字了,因为此时运行的线程是主线程main,main是static的,而getName是属于对象的,两者冲突,会报错,因此只能通过currentThread来获取线程的名称;

!!还有就是一旦执行了run(不管是start执行的还是直接手动调用的),那么调度器就会将该线程对象标记为运行状态,处于运行状态的线程对象无法再start,如果此时再调用start会抛出异常(线程状态异常);

!!记住:start对于一个线程对象,只能调用一次,多一次就会抛出线程状态异常;

    5) 为了证明start后是就绪状态(并不会立马执行线程),可以在start调用后立即跟一个输出语句,可以发现有时候start后面的输出语句会先输出(比start中的输出更早),因为调度是由调度器决定的,因此这种事不一定每次都发生;

!!因此,如果想在start后立马开始执行线程体可以在start调用后跟一个Thread.sleep(mili)让主线程休息一段事件,在休息的时间CPU一定会执行其它线程;


4. 运行(Running):即线程体进入CPU运行


5. 阻塞(Blocked):

    1) 是指正常运行的线程因为某种原因被打断了,然后暂时搁置在线程队列中(然后CPU资源让给其它线程);

    2) 注意!阻塞的2要素:

         i. 只能从运行状态进入阻塞状态;

         ii. 必须是正在运行的线程被中途打断暂时搁置;

    3) 线程如何进入阻塞状态:

         i. 线程主动调用sleep休息一段时间;

         ii. 调用了一个阻塞时IO方法,CPU转去处理IO操作;

         iii. 线程当前正视图访问一个同步监视器(即临界资源,多线程共享资源,但是有同步锁),但该同步器整备其它线程占用,因此就阻塞在那里等待解锁;

         iv. 线程正在等待某个通知(notify);

         v. 程序调用该线程对象的suspend方法将线程挂起,但这个方法容易导致死锁;

    4) 阻塞线程必定要接着重新运行,但不会立马就会从阻塞状态进入运行状态,因为被阻塞的线程可能会有很多(同时有很多线程被阻塞了),但一个CPU核只能一次执行一个线程,那么多线程同时运行是不可能的,因此阻塞线程在被运行之前还是先进入就绪状态等待调度;

!因此阻塞状态的下一状态必定是就绪状态;

   5) 转入就绪状态的时机:是和前面从运行状态进入阻塞状态相对应的

        i. sleep到时;

        ii. 阻塞IO完毕;

        iii. 获取到了同步监视器;

        iv. 接收到了通知;

        v. 调用线程对象的resume方法恢复了线程;


!如果调用了线程对象的yield方法会从运行状态直接进入就绪状态而不是阻塞状态,yield是让步的意思,因此可以理解为阻塞是大让步,yield是小让步,因此被yield的线程的重要性一般大于被阻塞的线程;


6. 死亡(Stop):

    1) 死亡时机:

        i. run/call执行完毕,线程的任务正常执行完毕,正常死亡;

        ii. 线程在执行过程中抛出了没有处理或者未捕获的异常,被强制死亡;

        iii. 直接手动调用线程对象的stop方法主动人为枪毙,但这种方法容易导致死锁,并不推荐:public final void Thread.stop();

    2) 检查线程是否活跃:

        i. 调用线程对象的isAlive方法:public final boolean Thread.isAlive();

        ii. 这不是检查是否死亡,而是是否活跃;

        iii. 当线程处于静态时(新建、死亡)表示不活跃(返回false),当线程处于动态时(就绪、运行、阻塞)表示活跃(返回true);

    3) 线程死亡后就仅仅是内存中的一块儿“肉”,即保存着数据的一块儿内存资源,立刻还可以利用的即时线程执行完毕后得到的计算结果罢了,结果利用完毕后几乎可以被清理(没有其它作用了);

    4) 不能对已死亡的线程调用start启动,会抛出线程状态异常,记住!start只能调用一回;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值