java线程状态 以及 sheep()、wait()、yield() 区别

前言

最近看到很多人都在讨论多线程的问题,于是写出了这篇博客,希望可以帮到正在学习和使用这块的朋友们,首先我们先看看两个图(两个图都来自其他码农的分享)

 

这两个图是一样的逻辑,这里一起罗列出来,下面让我们用语句来简单描述下两个图:

sleep 让线程从 【running】 -> 【阻塞态】 时间结束/interrupt -> 【runnable】
wait 让线程从 【running】 -> 【等待队列】notify -> 【锁池】 -> 【runnable】

当我们看到这个图的时候首先会看到线程的几种状态,下面让我们先来分别说明一下:

线程共包括一下5种状态:

1,新建、初始状态(New) :线程对象被创建后就进入了新建状态,Thread thread = new Thread();

2,就绪(Runnable):也被称之为“可执行状态”,当线程被new出来后,其他的线程调用了该对象的start()方法,即thread.start(),此时线程位于“可运行线程池”中,只等待获取CPU的使用权,随时可以被CPU调用。进入就绪状态的进程除CPU之外,其他运行所需的资源都已经全部获得。

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

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

阻塞的情况分三种:

(01) 等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池中”。进入这个状态后是不能自动唤醒的,必须依靠其他线程调用notify()或者notifyAll()方法才能被唤醒。

(02)同步阻塞:运行的线程在获取对象的(synchronized)同步锁时,若该同步锁被其他线程占用,则JVM会吧该线程放入“锁池”中。

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

以上三种阻塞状态请参考上面的2个图示来理解

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

 wait(), notify(), notifyAll()等方法介绍

这三个方法都是定义到Object类中,wait的作用是当当前线程释放它所持有的锁进入等待状态,而notify和notifyAll则是唤醒当前对象上的等待线程。

notify()        -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()    -- 唤醒在此对象监视器上等待的所有线程。

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

wait()会使“当前线程”等待,并且会释放到它所占用的“锁标志”,从而使线程所在对象中的其他synchronized数据可以被其他线程使用。

waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronizedblock中进行调用。如果在non-synchronized函数或non-synchronizedblock中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

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

suspend()和 resume()方法

两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型地,suspend()和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume()使其恢复。

注意区别:

初看起来wait() 和 notify() 方法与suspend()和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的suspend()及其它所有方法在线程阻塞时都不会释放占用的锁(如果占用了的话),而wait() 和 notify() 这一对方法则相反。

sleep() 和 yield()方法

这两个方法都定义在Thread.java中

sleep()的作用是让当前线程休眠(正在执行的线程主动让出cpu,然后cpu就可以去执行其他任务),即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时候会大于或者等于该休眠时间,当时间过后该线程重新被会形式,他会由“阻塞状态”编程“就绪状态”,从而等待cpu的调度执行,注意:sleep方法只是让出了cpu的执行权,并不会释放同步资源锁。

yield()的作用是让步,它能够让当前线程从“运行状态”进入到“就绪状态”,从而让其他等待线程获取执行权,但是不能保证在当前线程调用yield()之后,其他线程就一定能获得执行权,也有可能是当前线程又回到“运行状态”继续运行,注意:这里我将上面的“具有相同优先级”的线程直接改为了线程,很多资料都写的是让具有相同优先级的线程开始竞争,但其实不是这样的,优先级低的线程在拿到cpu执行权后也是可以执行,只不过优先级高的线程拿到cpu执行权的概率比较大而已,并不是一定能拿到。

举个例子:一帮朋友在排队上公交车,轮到Yield的时候,他突然说:我不想先上去了,咱们大家来竞赛上公交车。然后所有人就一块冲向公交车,

有可能是其他人先上车了,也有可能是Yield先上车了。

但是线程是有优先级的,优先级越高的人,就一定能第一个上车吗?这是不一定的,优先级高的人仅仅只是第一个上车的概率大了一点而已,

最终第一个上车的,也有可能是优先级最低的人。并且所谓的优先级执行,是在大量执行次数中才能体现出来的。

wait和sleep的区别

相同点:

1,他们都是在多线程的环境下,都可以在程序的调用出阻塞指定的毫秒数并且返回

2,两个方法都可以通过interrupt()方法打断线程的暂停状态,但是线程会抛出InterruptedException。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。

不同点:

1,Thread类的方法:sleep(),yield()
     Object的方法:wait()和notify()、notifyAll()

2,每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。 sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

3, wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 。注意:wiat()必须放在synchronized block中,否则会在program runtime时扔出“java.lang.IllegalMonitorStateException”异常。

4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

综上可得两者最大的区别:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。

注意:

第一:调用notify() 方法导致解除阻塞的线程是从因调用该对象的 wait()方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

第二:除了notify(),还有一个方法 notifyAll()也可起到类似作用,唯一的区别在于,调用 notifyAll()方法将把因调用该对象的 wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend()方法和不指定超时期限的wait()方法的调用都可能产生死锁。遗憾的是,Java并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

原文地址:https://www.cnblogs.com/blueskyli/p/10076975.html

时间: 2024-10-25 20:33:28

java线程状态 以及 sheep()、wait()、yield() 区别的相关文章

Java线程状态及Thread类中的主要方法

要想实现多线程,就必须在主线程中创建新的线程对象. 任何线程一般具有5种状态,即创建,就绪,运行,阻塞,终止. 创建状态: 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,他已经有了相应的内存空间和其他资源,但还处于不可运行状态. 就绪状态: 新建线程对象后,调用该线程的start()方法可以启动线程.当线程启动时,线程进入就绪状态.此时,线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件.-------(运行start()方法只是进入就绪状态,并没有开

谈谈 Java 线程状态相关的几个方法

http://blog.jrwang.me/2016/java-thread-states/ 发表于 2016-07-23 在 Java 多线程编程中,sleep(), interrupt(), wait(), notify() 等方法是非常基本也很常用的方法.这些方法会改变运行中的 Java 线程的状态,正确地认识这些方法是掌握 Java 并发编程的基本要求. Java 线程的状态 先来谈一谈 Java 中线程的状态.在 Java 中,线程的状态和底层操作系统中线程的状态并不是一一对应的关系,

学习java线程状态和看懂thread dump文件中的线程信息

线程的状态是一个很重要的东西,因此thread dump中会显示这些状态,通过对这些状态的分析,能够得出线程的运行状况,进而发现可能存在的问题.线程的状态在Thread.State这个枚举类型中定义: public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runn

关于Java线程状态

线程状态说明 线程状态定义在Thread.State枚举中,以下内容取自Thread.java源码 NEW: 线程尚未开始 A thread that has not yet started is in this state. RUNNABLE: 线程可运行,但可能未分配到处理器 A thread executing in the Java virtual machine is in this state. BLOCKED: 线程等待monitor锁 A thread that is block

Java线程状态、线程停止、线程阻塞

线程状态(五种状态) Java 线程的生命周期包括创建,就绪,运行,阻塞,死亡5 个状态.一个 Java 线程总是处于这 5 个生命周期状态之一,并在一定条件下可以在不同状态之间进行转换 . 创建状态 (New Thread) 在 Java 语言中使用 new操作符创建一个线程后,该线程仅仅是一个空对象,它具备了线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态. 就绪状态 (Runnable) 使用 start()方法启动一个线程后,系统为该线程分配了除 CPU 外的所需资源,

Java线程状态的转换

Java线程:线程状态的转换 一.线程状态 1.新状态:线程对象已经创建,还没有在其上调用start()方法. 2.可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态.当start()方法调用时,线程首先进入可运行状态.在线程运行之后或者从阻塞.等待或睡眠状态回来后,也返回到可运行状态. 3.运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态.这也是线程进入运行状态的唯一一种方式. 4.等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态.

Java 线程状态

1. 线程状态类型  1. 新建状态(New):新创建了一个线程对象.2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权.3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码.4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行.直到线程进入就绪状态,才有机会转到运行状态.阻塞的情况分三种:(一).等待阻塞:运行的线程执行wai

java线程状态及转换

java线程有6种状态: 新建线程new,启动线程runnable,阻塞block,限时等待timed_waiting,等待线程waiting,终止线程terminated 1.限时等待timed waiting :处于这种状态的线程不会被分配cpu执行时间.不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒.以下方法会让线程进行限时等待状态: a.  Thread.sleep()方法; b.  设置了Timeout参数的Object.wait()方法; c.  设置了Time

Java线程状态及其转换

线程状态及其转换 一.线程状态 Java中定义线程的状态有6种,可以查看Thread类的State枚举: public static enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; private State() {} } 初始(NEW):新创建了一个线程对象,还没调用start方法: 运行(RUNNABLE):java线程中将就绪(ready)和运行中(running)统称为运行(RUNNABLE