Java线程:线程状态的转换

系统管理员 2024-02-17 21:12 196阅读 0赞

本文转载至:http://lavasoft.blog.51cto.com/62575/99153

一. 线程状态类型

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
    (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
    二. 线程状态图

Center

下面谈谈如何让线程进入以上几种状态:

一、 新建状态(New):新创建了一个线程对象。

  1. // 线程 NEW 状态
  2. Thread one = new Thread();
  3. System.out.println("thread_one status is:" +
  4. one.getState().toString());

二、 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  1. // 线程RUNNABLE 状态
  2. Thread two = new Thread() {
  3. @Override
  4. public void run() {
  5. // TODO Auto-generated method stub
  6. for (int i = 0; i < 100; i++) {
  7. System.out.println("i value is :" + i);
  8. }
  9. }
  10. };
  11. two.start();
  12. System.out.println("thread_two status is:" + two.getState().toString());

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

第一种情况:对象锁定阻塞(锁定阻塞)

  1. final Object obj = new Object();
  2. Runnable block_one = new Runnable() {
  3. public void run() {
  4. synchronized (obj) {
  5. for (int i = 0; i < 1000; i++) {
  6. System.out.println("i value is:" + i);
  7. }
  8. }
  9. }
  10. };
  11. Thread thread_block_one = new Thread(block_one);
  12. Thread thread_block_two = new Thread(block_one);
  13. thread_block_one.start();
  14. thread_block_two.start();
  15. System.out.println("thread_block_one status is:"
  16. + thread_block_one.getState().toString());
  17. System.out.println("thread_block_two status is:"
  18. + thread_block_two.getState().toString());

运行截图:

Center 1

第二种:调用Thread.sleep()、Thread.join引发阻塞(其他阻塞)

  1. // 线程TIMED_WAITING 状态 :基于Thread.sleep
  2. Runnable block_one = new Runnable() {
  3. public void run() {
  4. for (int i = 0; i < 100; i++) {
  5. System.out.println("i value is:" + i);
  6. }
  7. }
  8. };
  9. Runnable block_two = new Runnable() {
  10. public void run() {
  11. for (int i = 0; i < 100; i++) {
  12. try {
  13. Thread.sleep(100);
  14. } catch (InterruptedException e) {
  15. // TODO Auto-generated catch block
  16. System.out
  17. .println("error message is:" + e.getMessage());
  18. }
  19. System.out.println("i value is:" + i);
  20. }
  21. }
  22. };
  23. Thread sleep_thread_block_one = new Thread(block_one);
  24. Thread sleep_thread_block_two = new Thread(block_two);
  25. sleep_thread_block_one.start();
  26. sleep_thread_block_two.start();
  27. System.out.println("thread_block_one status is:"
  28. + sleep_thread_block_one.getState().toString());
  29. System.out.println("thread_block_two status is:"
  30. + sleep_thread_block_two.getState().toString());
  31. // 线程TIMED_WAITING 状态 :基于Thread.join()
  32. Runnable block_one = new Runnable() {
  33. public void run() {
  34. for (int i = 0; i < 100; i++) {
  35. System.out.println("i value is:" + i);
  36. }
  37. }
  38. };
  39. Runnable block_two = new Runnable() {
  40. public void run() {
  41. for (int i = 0; i < 100; i++) {
  42. try {
  43. Thread.currentThread().join(1000);
  44. } catch (InterruptedException e) {
  45. // TODO Auto-generated catch block
  46. e.printStackTrace();
  47. System.out
  48. .println("error message is:" + e.getMessage());
  49. }
  50. System.out.println("i value is:" + i);
  51. }
  52. }
  53. };
  54. Thread sleep_thread_block_one = new Thread(block_one);
  55. Thread sleep_thread_block_two = new Thread(block_two);
  56. sleep_thread_block_one.start();
  57. sleep_thread_block_two.start();
  58. System.out.println("thread_block_one status is:"
  59. + sleep_thread_block_one.getState().toString());
  60. System.out.println("thread_block_two status is:"
  61. + sleep_thread_block_two.getState().toString());

运行截图:

Center 2

第三种:调用Object.wait(),进入等待阻塞

  1. final Object lock = new Object();
  2. Thread t1 = new Thread() {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 100; i++) {
  6. synchronized (lock) {
  7. try {
  8. lock.wait();
  9. } catch (InterruptedException e) {
  10. }
  11. System.out.println("t1 value is:" + i);
  12. }
  13. }
  14. }
  15. };
  16. Thread t2 = new Thread() {
  17. @Override
  18. public void run() {
  19. for (int j = 0; j < 100; j++) {
  20. synchronized (lock) {
  21. for (int i = 0; i < 10; i++) {
  22. System.out.println(i);
  23. }
  24. lock.notifyAll();
  25. }
  26. }
  27. }
  28. };
  29. t1.setName("one");
  30. t2.setName("two");
  31. t1.start();
  32. t2.start();
  33. System.out.println("t1 status is:" + t1.getState().toString());
  34. System.out.println("t2 status is:" + t2.getState().toString());

运行截图:

Center 3

总结和完善线程阻塞状态:Thread.sleep()、Thread.join()、Thread.yield()等方法。

1、Thread.sleep(睡眠方法)

Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。

睡眠的实现:调用静态方法。

try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。

注意:

1、线程睡眠是帮助所有线程获得运行机会的最好方法。

2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

3、sleep()是静态方法,只能控制当前正在运行的线程。

2、线程的优先级和线程让步yield()

线程的让步是通过Thread. yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。

要理解yield(),必须了解线程的优先级的概念。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。

注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。

当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。

设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如:

Thread t = new MyThread();
t.setPriority(8);
t.start();

线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。

线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:

static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。

3、Thread.yield()方法

Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

4、join()方法

Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:

Thread t = new MyThread();
t.start();
t.join();

另外,join()方法还有带超时限制的重载版本。 例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。

线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的。下面给示意图:

Center 4

小结

到目前位置,介绍了线程离开运行状态的3种方法:

1、调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)。

2、调用Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态,使得有相同优先级的线程有机会执行。

3、调用join()方法:保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。

除了以上三种方式外,还有下面几种特殊情况可能使线程离开运行状态:

1、线程的run()方法完成。

2、在对象上调用wait()方法(不是在线程上调用)。

3、线程不能在对象上获得锁定,它正试图运行该对象的方法代码。

4、线程调度程序可以决定将当前运行状态移动到可运行状态,以便让另一个线程获得运行机会,而不需要任何理由。

发表评论

表情:
评论列表 (有 0 条评论,196人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java线线状态转换

    一、线程生命周期的五种状态 线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。 新建状态(New):新创建了一个线程对象