Java中线程的状态及其转化

Wesley13
• 阅读 601

线程状态转化图:

Java中线程的状态及其转化

说明:

线程总共包括以下5种状态。

1、新状态New:该状态也叫新建状态,当线程对象被创建后,线程就进入了新建状态。例如:Thread thread = new Thread();。

2、就绪状态Runnable:该状态也被称为可执行状态。当线程对象被创建以后,其它线程调用了该对象的start()方法,从而来启用该线程。例如:thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3、运行状态(Running):线程获取CPU权限进行执行。但注意的是线程只能从就绪状态进入到运行状态。

4、阻塞状态(Blocked):阻塞状态时线程因为某种原因放弃了CPU的使用权,暂时停止运行。直到线程进入到就绪状态才有机会转到运行状态。

阻塞的情况有三种:

  (1) 等待阻塞--同过调用线程的wait()方法,让线程等待某工作的完成。

  (2) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。

  (3) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

一、wait(),notify(),notify(),和notifyAll()等方法介绍

      在Object.java中,定义了wait(), notify()和notifyAll()等接口。

      wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。

      notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

Object类中关于等待/唤醒的API详细信息如下:

notify()       -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()   -- 唤醒在此对象监视器上等待的所有线程。
wait()          -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)                   -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法                                                  或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状                                                        态”)。
wait(long timeout, int nanos)  -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

二、为什么notify(), wait()等函数定义在Object中,而不是Thread中。

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。

负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

三、 yield()介绍

yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

四、 yield() 与 wait()的比较

我们知道,wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而yield()的作用是让步,它也会让当前线程离开“运行状态”。它们的区别是:
(01) wait()是让线程由“运行状态”进入到“等待(阻塞)状态”,而不yield()是让线程由“运行状态”进入到“就绪状态”。
(02) wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。

 1 package com.zjk.thread;
 2 
 3 public class YieldLockTest {
 4         private  static Object obj = new Object();
 5         
 6         public static void main(String[] args) {
 7             ThreadA t1 = new ThreadA("t1");
 8             ThreadA t2 = new ThreadA("t2");
 9             t1.start();
10             t2.start();
11         }
12         
13         static class ThreadA extends Thread{
14             public ThreadA (String name) {
15                 super(name);
16             }        
17                 public void run () {
18                     //获取obj对象的同步
19                     synchronized (obj) {
20                           for(int i=0; i <10; i++){ 
21                                 System.out.printf("%s [%d]:%d\n", this.getName(), this.getPriority(), i); 
22                                 // i整除4时,调用yield
23                                 if (i%4 == 0)
24                                     Thread.yield();
25                             }
26                     }
27                 }
28         }
29         
30 }

输出结果:

 1 t1 [5]:0
 2 t1 [5]:1
 3 t1 [5]:2
 4 t1 [5]:3
 5 t1 [5]:4
 6 t1 [5]:5
 7 t1 [5]:6
 8 t1 [5]:7
 9 t1 [5]:8
10 t1 [5]:9
11 t2 [5]:0
12 t2 [5]:1
13 t2 [5]:2
14 t2 [5]:3
15 t2 [5]:4
16 t2 [5]:5
17 t2 [5]:6
18 t2 [5]:7
19 t2 [5]:8
20 t2 [5]:9

说明:

主线程main中启动了两个线程t1和t2。t1和t2在run()会引用同一个对象的同步锁,即synchronized(obj)。在t1运行过程中,虽然它会调用Thread.yield();但是,t2是不会获取cpu执行权的。因为,t1并没有释放“obj所持有的同步锁”!

五、sleep()介绍

sleep()定义在Thread.java中。

sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。

六、sleep()与wait()的比较

我们知道,wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。

点赞
收藏
评论区
推荐文章
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java多线程常见问题
Java多线程是什么Java提供的并发(同时、独立)处理多个任务的机制。多个线程共存于同一JVM进程里面,所以共用相同的内存空间,较之多进程,多线程之间的通信更轻量级。依我的理解,Java多线程完全就是为了提高CPU的利用率。Java的线程有4种状态,新建(New)、运行(Runnable)、阻塞(Blocked)、结束(Dead),关键就在于阻塞(Bl
Wesley13 Wesley13
3年前
java中线程的生命周期
java中线程的生命周期线程是java中绕不过去的一个话题,今天本文将会详细讲解java中线程的生命周期,希望可以给大家一些启发。java中Thread的状态java中Thread有6种状态,分别是:1.NEW新创建的Thread,还没有开始执行2.RUNNABLE可运行状态的T
Wesley13 Wesley13
3年前
java多线程(二)
一、摘要   当我们将线程创建并start时候,它不会一直占据着cpu执行,而是多个线程间会去执行着这个cpu,此时这些线程就会在多个状态之间进行着切换。  在线程的生命周期中,它会有5种状态,分别为:创建、准备、执行、阻塞、死亡二、线程生命周期状态详解  1、创建状态:其实就是newThread()创建了线程对象,这个时候JVM会为其分
Wesley13 Wesley13
3年前
java 面试知识点笔记(九)多线程与并发 下篇
问:线程有哪些状态?!(https://oscimg.oschina.net/oscnet/0643e1fd934e5c9047a9f4b4cc0bd8c54bc.jpg)1.新建(new):创建后尚未启动的线程的状态(新创建的线程但还没有调用start方法)2.运行(Runnable):包含Running和Ready(Runn
Wesley13 Wesley13
3年前
java多线程知识点总结
1\.基本知识点http://zangweiren.blog.51cto.com/412366/94386新建线程的两种方式,继续Thread类和实现runnable接口,启动线程用start方法线程状态:新建,运行,阻塞,等待,终止sleep方
Wesley13 Wesley13
3年前
java 线程及线程池
说明:线程共包括以下5种状态。1. 新建状态(New):线程对象被创建后,就进入了新建状态。例如,ThreadthreadnewThread()。2. 就绪状态(Runnable):也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread
Wesley13 Wesley13
3年前
Java线程的6种状态及切换(透彻讲解)
Java中线程的状态分为6种。1\.初始(NEW):新创建了一个线程对象,但还没有调用start()方法。2\.运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的
Wesley13 Wesley13
3年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Wesley13 Wesley13
3年前
Java多线程
JavaThread状态NEW创建被创建,但是还没有开始的线程,也就是还没有调用start()方法。RUNNABLE运行就绪可运行状态,在JVM中执行,但是可能等待操作系统CPU资源。BLOCKED阻塞线程阻塞等