浅谈Java的线程状态

Java中线程的状态分为6种,分别如下:

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。

这6种状态定义在Thread类的State枚举中,可查看源码进行一一对应。

  线程的状态图:

1. 初始状态

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

2.1. 就绪状态
就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
调用线程的start()方法,此线程进入就绪状态。
当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
锁池里的线程拿到对象锁后,进入就绪状态。
2.2. 运行中状态
线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

3. 阻塞状态

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

4. 等待

处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

5. 超时等待
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

6. 终止状态
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

常见的线程方法对比

sleep和wait的区别

我们都知道的是对于sleep和wait都是会让线程出现暂停执行的状态,下面从几个方面进行剖析个体区别

对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于Object 类中的。sleep()方法导致了程序暂停执行指定的时间,让出 cpu 给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用 sleep()方法的过程中,线程不会释放对象锁。而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。使用的位置不同:对于wait来说使用之前要获取到锁的存在,所以必须放在同步代码,或者同步中进行执行 但是 sleep来说可以放在任何的地方执行 。sleep需要捕获异常 。wait notify 等不需要这些。

start 和run的区别

start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。对于多线程来说只有真正意义上调用了start方法才算是对于线程的一个启动。方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程

join()

join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。就是说让该线程在执行完RUN()方法以后再执行join方法后面的代码,就是说可以让两个线程合并起来,用于实现同步功能,线程顺序执行。

yield()

该方法与sleep() 类似 只不过不能够由用户指定暂停多长的时间,并且yield ()方法只能让同优先级的线程有执行的机会。 前面提到了 sleep不会释放锁标识yield也不会释放锁标识。

实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。

sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。

wait()和notify()、notifyAll()

这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。wait() 方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。notifyAll() 从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

wait,notify阻塞唤醒确切过程?在哪阻塞,在哪唤醒?为什么要出现在同步代码块中,为什么要处于while循环中?

常见的 void wait 方法有

wait( long timeout )wait()。对于 无参的方法来说 : 在其他线程调用 此对象的notify 方法或者 nofifyall方法前 导致当前的线程处于等待的状态。

对于有参的函数来说 以上的两条成立的情况下 还会在时间超时之前也是处于等待的状态。

对于在执行完 wait方法以后。线程会释放掉所占用的锁标识 从而使线程所在的对象中的其他synchronized数据可被别的线程使用。 因为在执行wait 和 notify() 时候需要对锁标志进程处理和操作 一个是释放锁 一个是加锁 所以 就是来说 需要要在 synchronized函数中或者 函数块中进行调用,如果不在函数中 或是函数块中进行调用 虽然说可以编译通过。但是会出现 IllegalMonitorStateException异常。

wait,notify 和notifyAll 这些方法为什么不在 thread类里面

一个很明显的原因是Java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程来获得 由于 wait notify和notifyAll 都是锁级别的的操作,所以把他们定义在Object类中因为锁属于对象。

关于线程状态方面的基础知识,今天就总结这么多,自己对这一块,理解的还不透彻,后续进一步学习。

猜你喜欢

转载自blog.csdn.net/huzhiliayanghao/article/details/106503707