Java 线程
1. 创建和运行线程
1.1 直接使用 Thread
例如:
public class ThreadTest {public static void main(String[] args) {Thread t = new Thread() {public void run(){// 要执行的任务System.out.println(Thread.currentThread().getName()+" Running");}};// 启动线程t.setName("thread1-"); // 给线程起名字t.start();System.out.println(Thread.currentThread().getName());}
}
main
thread1- Running
1.2 使用Runnable 配合 Thread
将线程的创建和要执行的任务分开
- Thread 代表要执行的线程
- Runnable 可运行的任务[线程要执行的代码]
例如:
public class ThreadTest {public static void main(String[] args) {// 编写一个人任务Runnable task = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-"+"hello");}};// 创建一个线程,将任务交给线程Thread t = new Thread(task);t.setName("task-thread");t.start();System.out.println(Thread.currentThread().getName());}
}
main
task-thread-hello
1.3 Thread 和 Runnable 的联系
- 直接使用Thread 创建线程是将创建线程和任务逻辑编写在了一起,而Thread配合Runnable可以将任务逻辑和线程创建分割开来。
- 用Runnable 更容易与线程池等高级 API 进行配合。
- 用Runnable 让任务脱离了 Thread 继承体系,更灵活。
- Thread 继承了 Runnable 接口
1.4 FutureTask 配合 Thread
FutureTask 能够接收 Callable 类型的而参数,用来处理有返回结果的情况,例如:
public class ThreadTest {public static void main(String[] args) throws ExecutionException, InterruptedException {// FutureTask中的范形就是将来要返回结果的类型FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName()+" running...");Thread.sleep(1000);// 返回结果return 10086;}});Thread t = new Thread(task);t.setName("task-thread");t.start();// 使用主线程接收子线程运行后的结果System.out.println(Thread.currentThread().getName()+"线程接收到了"+t.getName()+"线程执行后的结果:"+task.get());}
}
task-thread running...
main线程接收到了task-thread线程执行后的结果:10086
2. 查看多个线程同时运行
编写一个还有多个线程的实例:
public class TestMultiThread {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println(Thread.currentThread().getName()+" running...");}}},"thread1").start();new Thread(new Runnable() {@Overridepublic void run() {while (true){System.out.println(Thread.currentThread().getName()+" running...");}}},"thread2").start();new Thread(new Runnable() {@Overridepublic void run() {while (true){System.out.println(Thread.currentThread().getName()+" running...");}}},"thread3").start();}
}
运行的部分结果如下:
...
thread1 running...
thread1 running...
thread1 running...
thread3 running...
thread3 running...
thread3 running...
thread3 running...
thread2 running...
thread2 running...
thread1 running...
......
由结果看出,线程运行的先后顺序是无法确定的,这取决于CPU对线程的调度。
3. 查看进程线程的方法
在不同的操作系统下查看线程进程是不一样的。
3.1 Windows
-
打开任务管理器就可以查看线程和进程数,也可以用来杀死进程。
-
**tasklist **查看进程
-
**taskkill **杀死进程
3.2 Linux
-
**ps -fe **查看所有进程
-
**ps -fT -p
: **产看某个进程的所有线程 -
kill :杀死进程
-
**top: **按照大写H 切换是否显示线程\
3.3 Java
- **jps: **命令查看所有java进程
- jstack
查看某个Java 进程(PID)的所有线程状态 - **jconsole: **查看某个Java进程中线程的运行情况
4. 线程运行的原理
4.1 栈与栈帧
Java Virtual Machine Stacks(Java 虚拟机栈)
我们都知道 JVM 中由堆,栈,方法区所组成,其中的栈内存是给线程使用的,每个线程启动后,虚拟机就会分配一块栈内存。
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。
- 每个线程都只能有一个活动栈帧,对迎着当前正在执行的那个方法
4.2 线程上下文切换(Thread Context Switch)
因为以下原因导致cpu不能再继续执行当前线程,转而去执行另外一个线程的代码:
- 当前线程的时间片用完
- 垃圾回收
- 有更高优先级的线程需要运行
- 线程自己调用了 sleep, yield, wait, join, park, synchronized, lock等方法
当上下文发生切换时,需要操作系统保存当前线程的信息,并恢复另外一个线程的状态继续执行。Java对应的概念就是程序计数器,它的作用就是记住下一条JVM指令的执行地址,是线程私有的。
- 状态包括程序计数器,虚拟机栈中的每个栈帧信息,如局部变量表,操作数栈,返回地址等。
- 发生上下文切换太频繁会影响性能
5. Java线程常见的方法
方法名 static | 功能说明 | 注意 |
---|---|---|
start() | 启动一个新线程,在新的线程运行run方法中的代码 | start方法只是让线程处于就绪态,里面的代码不一定会立刻执行,需要等到线程调度器将CPU时间片分配给该线程才会运行。该方法只会调用一次,调用多次会抛出非法线程状态异常 |
run() | 信线程启动后会调用的方法(一般是自己编写的逻辑) | |
join() | 等待线程运行结束 | |
join(long n) | 等待线程运行结束, 最多等待 n ms | |
getId() | 获取线程ID | 线程ID是唯一的 |
getName() | 获取线程名字 | |
setName(String) | 设置线程名字 | |
getPriority() | 获取线程优先级 | java 规定线程的而优先级是 1-10 的整数,比较大的优先级会提高该线程会被CPU优先调度 |
setPriority(int) | 设置线程优先级 | |
getState() | 获取线程状态 | Java中的线程状态一共有6种(用枚举表示):NEW, RUNNABLE, BLOCKED, WAITTING, TIMED_WAITTING, TERMINATED |
isInterrupted() | 判断是否被打断 | 不会清除打断标记 |
isAlive | 判断线程是否存活(还没有运行完毕) | |
interrupt() | 打断线程 | 打断当前线程处于运行的状态,并设置打断标记 |
isInterrupt() | 判断当前线程是否被打断 | |
currentThread | 获取当前正在运行的线程 | |
sleep(long n) | 让当前正在运行的线程暂时放弃CPU的使用权 | |
yield() | 提示线程调度器让出当前运行的线程,释放出CPU资源 | 主要是为了测试和调试 |
6. start 与 run
start表示启动一个线程,而run是线程启动后要执行的逻辑代码。
那么我们能不能不经过start而直接调用run方法呢?例如下面的代码:
public class TestThread4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"执行run方法");}},"thread");t.run();}
}
运行结果如下:
main执行run方法Process finished with exit code 0
我们发现:run()方法确实被执行的,但是是由主方法调用的run()方法,而不是子线程调用的run()方法!
7. sleep 与 yield
7.1 sleep
- 调用sleep 会让当前线程从RUNNING 进入到 TIMED_WAITTING 状态
- 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出打断异常
- 睡眠结束后的线程未必会立刻执行,状态只是从 TIMED_WAITTING 进入到
- 建议使用 TimeUnit 的sleep 来代替 Thread 的 sleep 开火的更好的可读性
调用sleep 进 TIMED_WAITTING 状态:
public class ThreadTest5 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {System.out.println(Thread.currentThread().getState());Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"thread1");System.out.println(t1.getState());t1.start();}
}
正在睡眠的线程可以被其它线程打断:
public class ThreadTest5 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {System.out.println("t1线程进入睡眠...");Thread.sleep(5000);} catch (InterruptedException e) {System.out.println("线程t1被线程main打断唤醒");throw new RuntimeException(e);}}},"t1");t1.start();Thread.sleep(1000);System.out.println("打断线程t1");t1.interrupt();}
}
运行结果如下:
t1线程进入睡眠...
打断线程t1
线程t1被线程main打断唤醒
Exception in thread "t1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interruptedat com.cherry.a03.ThreadTest5$1.run(ThreadTest5.java:18)at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.InterruptedException: sleep interruptedat java.base/java.lang.Thread.sleep0(Native Method)at java.base/java.lang.Thread.sleep(Thread.java:509)at com.cherry.a03.ThreadTest5$1.run(ThreadTest5.java:15)... 1 moreProcess finished with exit code 0
TimeUnit 的sleep 相比于 Thread.sleep 更好一些,好在 TimeUnit 可以指定时间的单位(把时间分成了不同的单位),可读性更好:
public enum TimeUnit {/*** Time unit representing one thousandth of a microsecond.*/NANOSECONDS(TimeUnit.NANO_SCALE),/*** Time unit representing one thousandth of a millisecond.*/MICROSECONDS(TimeUnit.MICRO_SCALE),/*** Time unit representing one thousandth of a second.*/MILLISECONDS(TimeUnit.MILLI_SCALE),/*** Time unit representing one second.*/SECONDS(TimeUnit.SECOND_SCALE),/*** Time unit representing sixty seconds.* @since 1.6*/MINUTES(TimeUnit.MINUTE_SCALE),/*** Time unit representing sixty minutes.* @since 1.6*/HOURS(TimeUnit.HOUR_SCALE),/*** Time unit representing twenty four hours.* @since 1.6*/DAYS(TimeUnit.DAY_SCALE);......
}
简单用法如下:
public class ThreadTest5 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {// 子线程睡眠2sTimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"t1");t1.start();}
}
7.2 yield
- 调用 yield 会让当前线程从Running进入到 Runnable 就绪状态,然后调度执行其它线程
- 具体的实现依赖于操作系统的线程调度
8. 线程优先级
- 线程优先级会提示(hint) 调度器优先调度该线程,但它仅仅只是一个提示,调度器可以忽略它
- 如果CPU比较繁忙,那么优先级高的线程会获得更多的时间片,但CPU空闲时,优先级几乎没作用
不加yield:
public class yieldTest1 {public static void main(String[] args) {Runnable task1 = new Runnable() {@Overridepublic void run() {int t1_count = 0;for(;;){System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));}}};Runnable task2 = new Runnable() {@Overridepublic void run() {int t2_count = 0;for(;;){System.out.println(" --->"+Thread.currentThread().getName()+" "+(t2_count++));}}};Thread t1 = new Thread(task1,"thread1");Thread t2 = new Thread(task2,"thread2");t1.start();t2.start();}
}
运行结果部分如下:
--->thread2 54537--->thread2 54538
--->thread1 50531
--->thread1 50532--->thread2 54539--->thread2 54540
我们发现不是用yield两个线程的计算机结果是差不多的。
接下来我们使用yield再次运行:
public class yieldTest1 {public static void main(String[] args) {Runnable task1 = new Runnable() {@Overridepublic void run() {int t1_count = 0;for(;;){System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));}}};Runnable task2 = new Runnable() {@Overridepublic void run() {int t2_count = 0;for(;;){Thread.yield(); // 让出t2线程 --> 线程t1会占用大部分的时间片,而线程t2只会占用少部分时间片// yield 让出线程并不一定会成功,最终还是操作系统的线程调度器考虑究竟是否让出线程System.out.println(" --->"+Thread.currentThread().getName()+" "+(t2_count++));}}};Thread t1 = new Thread(task1,"thread1");Thread t2 = new Thread(task2,"thread2");t1.start();t2.start();}
}
--->thread1 91806
--->thread1 91807
--->thread1 91808
--->thread1 91809
--->thread1 91810--->thread2 41088--->thread2 41089
接着我们不使用yield让出线程,而是使用线程优先级查看一下线程调度:
public class yieldTest1 {public static void main(String[] args) {Runnable task1 = new Runnable() {@Overridepublic void run() {int t1_count = 0;for(;;){System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));}}};Runnable task2 = new Runnable() {@Overridepublic void run() {int t2_count = 0;for(;;){System.out.println(" --->"+Thread.currentThread().getName()+" "+(t2_count++));}}};Thread t1 = new Thread(task1,"thread1");Thread t2 = new Thread(task2,"thread2");t1.setPriority(Thread.MIN_PRIORITY); // 线程1设置最低优先级t2.setPriority(Thread.MAX_PRIORITY); // 线程2设置最高优先级t1.start();t2.start();}
}
--->thread1 68186
--->thread1 68187
--->thread1 68188
--->thread1 68189--->thread2 74237--->thread2 74238--->thread2 74239--->thread2 74240
我们发现,这优先级其实并不明显,因为哪个线程优先被分配时间片最终还是取决于操作系统的线程调度器。
9. sleep 应用
9.1 sleep实现
在没有利用 cpu 计算时,不要使用 while(true) 空转浪费 cpu 资源,这时可以使用 yield 或者 sleep 让出 cpu 资源分配给其它线程或进程:
package com.cherry.a03;/*** @author: lily* @data: 2024.06.11* @description:*/
public class SleepTest1 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {while(true){}}});t1.start();}
}
如果不使用Thread.sleep(xxx),子线程就会一直占用CPU资源,如果该线程跑在单核CPU上,会瞬间将CPU跑满,其它线程就不会得到CPU资源。因此,最好是在while(true)中加入sleep, 休眠时间大小可自行设置,避免空转导致占满了CPU资源。
10. join 方法解析
join()方法是等待线程运行结束。首先观察如下代码:
public class Test10 {private static int count = 0;public static void main(String[] args) {test1();}public static void test1(){System.out.println(Thread.currentThread().getName()+"开始");Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"开始");try {sleep(1000);count = 10;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {System.out.println(Thread.currentThread().getName()+"结束");}}},"thread1");t1.start();System.out.println("count结果为:"+count);System.out.println(Thread.currentThread().getName()+"结束");}
}
结果为:
main开始
thread1开始
count结果为:0
main结束
thread1结束Process finished with exit code 0
我们发现,由于主线程和子线程是并行运行的,子线程还没运行完主线程就已经运行结束了。
如果我们要等待子线程运行完成后,才能让主线程完成运行,这就需要我们主线程要等待子线程运行,可以在 t1.start() 后使用 join() 方法。如下所示:
public class Test10 {private static int count = 0;public static void main(String[] args) throws InterruptedException {test1();}public static void test1() throws InterruptedException {System.out.println(Thread.currentThread().getName()+"开始");Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"开始");try {sleep(1000);count = 10;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {System.out.println(Thread.currentThread().getName()+"结束");}}},"thread1");t1.start();// 等待 t1 线程运行结束t1.join();System.out.println("count结果为:"+count);System.out.println(Thread.currentThread().getName()+"结束");}
}
运行结果如下:
main开始
thread1开始
thread1结束
count结果为:10
main结束Process finished with exit code 0
10. 1 应用之同步
从调用者的角度来讲:
- 需要等待结果返回,才能继续运行就是同步
- 不需要等待结果返回,就能继续运行就是异步
如果我不止想等待一个程序的运行结果,而是多个程序的运行结果,那就调用不同线程的join方法,例如:
public class Test11 {private static int a;private static int b;private static int c;public static void main(String[] args) throws InterruptedException {Runnable task1 = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread()+"开始运行...");try {Thread.sleep(1000);a = 10;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {System.out.println(Thread.currentThread()+"结束运行...");}}};Runnable task2 = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread()+"开始运行...");try {Thread.sleep(2000);b = 20;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {System.out.println(Thread.currentThread()+"结束运行...");}}};Runnable task3 = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread()+"开始运行...");try {Thread.sleep(3000);c = 30;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {System.out.println(Thread.currentThread()+"结束运行...");}}};Thread t1 = new Thread(task1,"thread1");Thread t2 = new Thread(task2,"thread2");Thread t3 = new Thread(task3,"thread3");t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();System.out.println(a+b+c);}
}
运行结果如下:
Thread[#22,thread2,5,main]开始运行...
Thread[#21,thread1,5,main]开始运行...
Thread[#23,thread3,5,main]开始运行...
Thread[#21,thread1,5,main]结束运行...
Thread[#22,thread2,5,main]结束运行...
Thread[#23,thread3,5,main]结束运行...
60Process finished with exit code 0
10.2 有时效的join
join(long n): 线程最多会等待 n ms,如果等待的时间超过了n ms, 就不会继续等下去。例如:
public class Test12 {private static int count = 10;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);count = 30;} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();t.join(1000);System.out.println(count);}
}
运行结果如下:
10Process finished with exit code 0
子线程需要休眠3秒才能拿到结果,但是子线程只能等待1秒,因此子线程等待结束后并不会拿到最终的结果!
11. interrupt 方法详解
11.1 打断 sleep, wait, join的线程
打断 sleep 线程,会清空打断状态,以 sleep 为例:
public class SleepTest3 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"thread1");t1.start();TimeUnit.MILLISECONDS.sleep(100);t1.interrupt(); // 打断线程t1的 sleep 状态System.out.println("打断状态:"+t1.isInterrupted());}
}
public class SleepTest3 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"thread1");t1.start();TimeUnit.MILLISECONDS.sleep(100);t1.interrupt(); // 打断线程t1 sleep 状态System.out.println("打断状态:"+t1.isInterrupted());}
}
Exception in thread "thread1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interruptedat com.cherry.a03.SleepTest3$1.run(SleepTest3.java:20)at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.InterruptedException: sleep interruptedat java.base/java.lang.Thread.sleep0(Native Method)at java.base/java.lang.Thread.sleep(Thread.java:558)at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)at com.cherry.a03.SleepTest3$1.run(SleepTest3.java:18)... 1 more
打断状态:trueProcess finished with exit code 0
打断正常运行的线程,不会清空打断状态:
public class ThreadTest15 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {while (true){if(Thread.currentThread().isInterrupted()){System.out.println("正在运行的线程被打断了,exit");break;}}}});t1.start();TimeUnit.MILLISECONDS.sleep(100);System.out.println("interrupt");t1.interrupt();}
}
运行结果如下:
interrupt
正在运行的线程被打断了,exitProcess finished with exit code 0
11.2 两阶段终止模式
如何在一个线程中优雅的终止另一个线程
错误思想:
- 使用线程对象的stop()方法:stop()方法会真正的杀死线程,如果此时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁了,其他线程将永远无法获得该资源的锁。
- 使用 System.exit() 方法停止线程:目的仅仅是停止一个线程,但这种做法会让整个程序(进程)都停止。
正确的多线程终止应如下所示:
代码实现如下:
public class Test04 {public static void main(String[] args) throws InterruptedException {Monitor monitor = new Monitor();monitor.start();// 主线程过3500ms后将监控程序优雅的停止下来Thread.sleep(3500);monitor.stop();}
}// 监控类
class Monitor {private Thread monitorThread; // 监控线程// 启动监控线程public void start(){monitorThread = new Thread(new Runnable() {@Overridepublic void run() {while (true){// 获取当前线程的打断状态Thread t1 = Thread.currentThread();if(t1.isInterrupted()){// 如果被打断System.out.println("料理后世");break;}try {TimeUnit.SECONDS.sleep(1);System.out.println("执行监控记录");} catch (InterruptedException e) {e.printStackTrace();// 重新设置打断标记Thread.currentThread().interrupt();}}}});monitorThread.start();}// 停止监控线程public void stop(){monitorThread.interrupt();}
}
运行结果如下:
执行监控记录
执行监控记录
执行监控记录
java.lang.InterruptedException: sleep interruptedat java.base/java.lang.Thread.sleep0(Native Method)at java.base/java.lang.Thread.sleep(Thread.java:558)at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)at com.cherry.a03.Monitor$1.run(Test04.java:39)at java.base/java.lang.Thread.run(Thread.java:1583)
料理后世Process finished with exit code 0
12. 主线程与守护线程
默认情况下,Java 进程需要等待所有线程都运行结束,Java进程才会停止运行,有一种特殊的线程叫做守护线程,只有它非守护线程运行都运行结束了,即使守护线程的代码没有执行完,也会强制结束。
例如:
public class Test6 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {while (true){if(Thread.currentThread().isInterrupted()){break;}}}});t1.setDaemon(true); //设置t1线程为守护线程t1.start();Thread.sleep(1000);System.out.println("结束");}
}
注意:
- 垃圾回收器就是一种守护线程
- Tomact中的Acceptor 和 Poller 线程也都是守护线程
13 Java层面的线程状态
根据 ThreadState 枚举,分为6种状态:
- NEW 线程刚被创建,但是还没调用start()方法
- RUNNABLE: 当调用了start()方法后,注意,Java API层面的runnable包含了操作系统层面的[就绪态],[运行态], [阻塞态]。
public class TestState {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("running...");}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {while (true){// runnable}}});t2.start();Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("running..."); // terminated}});t3.start();Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (TestState.class){try{Thread.sleep(1000000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t4.start();Thread t5 = new Thread(new Runnable() {@Overridepublic void run() {try{t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t5.start();Thread t6 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (TestState.class){ // 没办法拿到锁 BLOCKtry {Thread.sleep(100000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t6.start();try{Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 state:"+t1.getState());System.out.println("t2 state:"+t2.getState());System.out.println("t3 state:"+t3.getState());System.out.println("t4 state:"+t4.getState());System.out.println("t5 state:"+t5.getState());System.out.println("t6 state:"+t6.getState());}
}
结果如下:
running...
t1 state:NEW
t2 state:RUNNABLE
t3 state:TERMINATED
t4 state:TIMED_WAITING
t5 state:WAITING
t6 state:BLOCKED