Java 并发编程学习09 | java线程(上)总结

java 线程的状态

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

6. 终止(TERMINATED):表示该线程已经执行完毕。

 

 

1. 初始状态

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

2.1. 就绪状态

  1. 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
  2. 调用线程的start()方法,此线程进入就绪状态。
  3. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
  4. 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
  5. 锁池里的线程拿到对象锁后,进入就绪状态。

2.2. 运行中状态

线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

Java 语言里则把可运行状态和运行状态合并了,这两个状态在操作系统调度层面有用,而 JVM 层面不关心这两个状态,因为 JVM 把线程调度交给操作系统处理了。

3. 阻塞状态

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

4. 等待

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

5. 超时等待

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

6. 终止状态

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

 

 

让线程进入以上几种状态

1. NEW, 这个最简单了,  
1     static void NEW() {
2         Thread t = new Thread();
3         System.out.println(t.getState());
4     }
输出NEW
 
2. RUNNABLE, 也简单, 让一个thread start, 同时代码里面不要sleep或者wait等
 1     private static void RUNNABLE() {
 2         Thread t = new Thread() {
 3             public void run() {
 4                 for (int i = 0; i < 1; i++) {
 5                     System.out.println(this.getState());
 6                 }
 7             }
 8         };
 9         t.start();
10     }

 

 

3. BLOCKED, 这个就必须至少两个线程以上, 然后互相等待synchronized 块

 1     // bolcked
 2     private static void BLOCKED() {
 3         final Object lock = new Object();
 4         Runnable run = new Runnable() {
 5             @Override
 6             public void run() {
 7                 System.out.println(Thread.currentThread().getState());
 8                 for (int i = 0; i < Integer.MAX_VALUE; i++) {
 9                     synchronized (lock) {
10                         if(i % 1000000 == 0){
11                             System.out.println(i);
12                         }
13                     }
14                 }
15             }
16         };
17 
18         Thread t1 = new Thread(run);
19         t1.setName("t1");
20         Thread t2 = new Thread(run);
21         t2.setName("t2");
22 
23         t1.start();
24         t2.start();
25     }

这时候, 一个在RUNNABLE, 另一个就会在BLOCKED (等待另一个线程的 System.out.println.. 这是个IO操作, 属于系统资源, 不会造成WAITING等)

 

4. WAITING, 这个需要用到生产者消费者模型, 当生产者生产过慢的时候, 消费者就会等待生产者的下一次notify

 1     private static void WAITING() {
 2 
 3         final Object lock = new Object();
 4         Thread t1 = new Thread() {
 5             @Override
 6             public void run() {
 7                 int i = 0;
 8                 while (true) {
 9                     synchronized (lock) {
10                         try {
11                             lock.wait();
12                         } catch (InterruptedException e) {
13                         }
14                         System.out.println(i++);
15                     }
16                 }
17             }
18         };
19 
20         Thread t2 = new Thread() {
21             @Override
22             public void run() {
23                 while (true) {
24                     synchronized (lock) {
25                         for (int i = 0; i < 10000000; i++) {
26                             System.out.println(i);
27                         }
28                         lock.notifyAll();
29                     }
30                 }
31             }
32         };
33 
34         t1.setName("^^t1^^");
35         t2.setName("^^t2^^");
36 
37         t1.start();
38         t2.start();
39     }

 

5. TIMED_WAITING, 这个仅需要在4的基础上, 在wait方法加上一个时间参数进行限制就OK了.
 
把4中的synchronized 块改成如下就可以了.
1 synchronized (lock) {
2    try {
3       lock.wait(60 * 1000L);
4    } catch (InterruptedException e) {
5    }
6    System. out .println(i++);
7  }

另外看stack的输出,  他叫 TIMED_WAITING(on  object monitor) , 说明括号后面还有其他的情况, 比如sleep, 我们直接把t2的for循环改成sleep试试:
 
1 synchronized (lock) {
2     try {
3           sleep(30*1000L);
4     } catch (InterruptedException e) {
5     }
6     lock.notifyAll();
7 }

看到了吧, t2的state是 TIMED_WAITING( sleeping),  而t1依然是on object monitor , 因为t1还是wait在等待t2 notify, 而t2是自己sleep
另外, join操作也是进入 on object monitor
 
6. TERMINATED, 这个状态只要线程结束了run方法, 就会进入了…
 1     private static void TERMINATED() {
 2         Thread t1 = new Thread();
 3         t1.start();
 4         System.out.println(t1.getState());
 5         try {
 6             Thread.sleep(1000L);
 7         } catch (InterruptedException e) {
 8         }
 9         System.out.println(t1.getState());
10     }

由于线程的start方法是异步启动的, 所以在其执行后立即获取状态有可能才刚进入RUN方法且还未执行完毕.

 

完整代码如下,顺带打印了pid,可以用 jstack pid 来查看信息

  1 package com.test.thread;
  2 
  3 import java.lang.management.ManagementFactory;
  4 
  5 public class TestThread {
  6     // 初始状态
  7     static void NEW() {
  8         Thread t = new Thread();
  9         System.out.println(t.getState());
 10     }
 11 
 12     // 运行状态
 13     private static void RUNNABLE() {
 14         Thread t = new Thread() {
 15             public void run() {
 16                 for (int i = 0; i < 1; i++) {
 17                     System.out.println(this.getState());
 18                 }
 19             }
 20         };
 21         t.start();
 22     }
 23 
 24     // bolcked
 25     private static void BLOCKED() {
 26         final Object lock = new Object();
 27         Runnable run = new Runnable() {
 28             @Override
 29             public void run() {
 30                 System.out.println(Thread.currentThread().getState());
 31                 for (int i = 0; i < Integer.MAX_VALUE; i++) {
 32                     synchronized (lock) {
 33                         if(i % 1000000 == 0){
 34                             System.out.println(i);
 35                         }
 36                     }
 37                 }
 38             }
 39         };
 40 
 41         Thread t1 = new Thread(run);
 42         t1.setName("t1");
 43         Thread t2 = new Thread(run);
 44         t2.setName("t2");
 45 
 46         t1.start();
 47         t2.start();
 48     }
 49 
 50     private static void WAITING() {
 51 
 52         final Object lock = new Object();
 53         Thread t1 = new Thread() {
 54             @Override
 55             public void run() {
 56                 int i = 0;
 57                 while (true) {
 58                     synchronized (lock) {
 59                         try {
 60                             lock.wait();
 61                         } catch (InterruptedException e) {
 62                         }
 63                         System.out.println(i++);
 64                     }
 65                 }
 66             }
 67         };
 68 
 69         Thread t2 = new Thread() {
 70             @Override
 71             public void run() {
 72                 while (true) {
 73                     synchronized (lock) {
 74                         for (int i = 0; i < 10000000; i++) {
 75                             System.out.println(i);
 76                         }
 77                         lock.notifyAll();
 78                     }
 79                 }
 80             }
 81         };
 82 
 83         t1.setName("^^t1^^");
 84         t2.setName("^^t2^^");
 85 
 86         t1.start();
 87         t2.start();
 88     }
 89 
 90     private static void TIMED_WAITING() {
 91 
 92         final Object lock = new Object();
 93         Thread t1 = new Thread() {
 94             @Override
 95             public void run() {
 96                 int i = 0;
 97                 while (true) {
 98                     synchronized (lock) {
 99                         try {
100                             lock.wait(60 * 1000L);
101                         } catch (InterruptedException e) {
102                         }
103                         System.out.println(i++);
104                     }
105                 }
106             }
107         };
108 
109         Thread t2 = new Thread() {
110             @Override
111             public void run() {
112                 while (true) {
113                     synchronized (lock) {
114                         for (int i = 0; i < 10000000; i++) {
115                             System.out.println(i);
116                         }
117                         lock.notifyAll();
118                     }
119                 }
120             }
121         };
122 
123         t1.setName("^^t1^^");
124         t2.setName("^^t2^^");
125 
126         t1.start();
127         t2.start();
128     }
129 
130     private static void TERMINATED() {
131         Thread t1 = new Thread();
132         t1.start();
133         System.out.println(t1.getState());
134         try {
135             Thread.sleep(1000L);
136         } catch (InterruptedException e) {
137         }
138         System.out.println(t1.getState());
139     }
140 
141 
142     public static void main(String[] args) {
143         String name = ManagementFactory.getRuntimeMXBean().getName();
144         System.out.println(name);
145         String[] names = name.split("@");
146         int pid = Integer.parseInt(names[0]);
147         System.out.println("pid: " + pid);
148 
149         NEW();
150         RUNNABLE();
151         BLOCKED();
152 
153         TERMINATED();
154     }
155 }
View Code

 

 

了解线程的状态究竟有什么用

一句话, 在找到系统中的潜在性能瓶颈有作用。
 
当java系统运行慢的时候, 我们想到的应该先找到性能的瓶颈, 而jstack等工具, 通过jvm当前的stack可以看到当前整个vm所有线程的状态, 当我们看到一个线程状态经常处于
WAITING 或者 BLOCKED的时候, 要小心了, 他可能在等待资源经常没有得到释放(当然, 线程池的调度用的也是各种队列各种锁, 要区分一下, 比如下图)
6db341bbd7680bbc2e6ae37a66329397
 
 
这是个经典的并发包里面的线程池, 其调度队列用的是LinkedBlockingQueue, 执行take的时候会block住, 等待下一个任务进入队列中, 然后进入执行, 这种理论上不是系统的性能瓶颈, 找瓶颈一般先找自己的代码stack,再去排查那些开源的组件/JDK的问题
 
排查问题的几个思路:
0. 如何跟踪一个线程?
看到上面的stack输出没有, 第一行是内容是 threadName priority tid nid desc
更过跟踪tid, nid 都可以唯一找到该线程.
1. 发现有线程进入BLOCK, 而且持续好久, 这说明性能瓶颈存在于synchronized块中, 因为他一直block住, 进不去, 说明另一个线程一直没有处理好, 也就这个synchronized块中处理速度比较慢, 然后再深入查看. 当然也有可能同时block的线程太多, 排队太久造成.
2. 发现有线程进入WAITING, 而且持续好久, 说明性能瓶颈存在于触发notify的那段逻辑. 当然还有就是同时WAITING的线程过多, 老是等不到释放.
3. 线程进入TIME_WAITING 状态且持续好久的, 跟2的排查方式一样.
 
上面的黑底白字截图都是通过jstack打印出来的, 可以直接定位到你想知道的线程的执行栈, 这对java性能瓶颈的分析是有极大作用的.
 
NOTE: 上面所有代码都是为了跟踪线程的状态而写的, 千万不要在线上应用中这么写…
posted @ 2022-02-23 22:18  r1-12king  阅读(41)  评论(0编辑  收藏  举报