1_JAVA线程

news/2024/10/5 9:21:15

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 的联系

  1. 直接使用Thread 创建线程是将创建线程和任务逻辑编写在了一起,而Thread配合Runnable可以将任务逻辑和线程创建分割开来。
  2. 用Runnable 更容易与线程池等高级 API 进行配合。
  3. 用Runnable 让任务脱离了 Thread 继承体系,更灵活。
  4. 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

  1. 调用sleep 会让当前线程从RUNNING 进入到 TIMED_WAITTING 状态
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出打断异常
  3. 睡眠结束后的线程未必会立刻执行,状态只是从 TIMED_WAITTING 进入到
  4. 建议使用 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 应用之同步

从调用者的角度来讲:

  • 需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步

image-20240611150635995

如果我不止想等待一个程序的运行结果,而是多个程序的运行结果,那就调用不同线程的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 两阶段终止模式

如何在一个线程中优雅的终止另一个线程

错误思想:

  1. 使用线程对象的stop()方法:stop()方法会真正的杀死线程,如果此时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁了,其他线程将永远无法获得该资源的锁。
  2. 使用 System.exit() 方法停止线程:目的仅仅是停止一个线程,但这种做法会让整个程序(进程)都停止。

正确的多线程终止应如下所示:

image-20240611160116390

代码实现如下:

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种状态:

image-20240611173953437

  • 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/44180.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Go变量作用域精讲及代码实战

关注作者,复旦AI博士,分享AI领域与云服务领域全维度开发技术。拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕博,复旦机器人智能实验室成员,国家级大学生赛事评审专家,发表多篇SCI核心期刊学术论文,阿里云认证的资深架构师,项目管理专业人士,上…

C# JavaScriptSerializer序列化时的时间处理详解

原文链接:https://www.jb51.net/article/122143.htm输出如下图所示: 猜测这里是由于js初始化时间的时候往往是向 1970/01/01 添加毫秒数,JavaScriptSerializer进行序列化的时候也会格式化为距离1970/01/01 到当该时间点GMT+0 时间的毫秒数, 如果直接反序列化可以看到少了8…

人工智能ChatGPT的多种应用:如何更好地提问

简介 ChatGPT 的主要优点之一是它能够理解和响应自然语言输入。在日常生活中,沟通本来就是很重要的一门课程,沟通的过程中表达的越清晰,给到的信息越多,那么沟通就越顺畅。 和 ChatGPT 沟通也是同样的道理,如果想要 ChatGPT 给到的信息越准确,越清晰,和它的沟通就至关重…

SQLite 自动插入时间戳

CREATE TABLE t1 (id INTEGER PRIMARY KEY,node_addr INT,channel_id INT,value INT,date TIMESTAMP NOT NULLDEFAULT (datetime(now, localtime) ) );

python-识别图片中的文字

1、下载:https://digi.bib.uni-mannheim.de/tesseract/ 我们之所以要应用Tesseract,是因为他是一个开源的OCR(光学字符识别)引擎,它可以从各种图像中提取文本信息。它具有以下作用:- 从扫描或拍摄的图像中提取文本:Tesseract可以从这些非结构化的图像中识别和提取文本,…

智能编程支持分析本地工程文件,项目级开发效率再度提升!

CodeGeeX的智能问答新增关联本地文件的系列功能,用户在智能问答的输入框中输入@,就可以在弹出框中选择需要使用的指令。从而能够针对所关联的文件给出更有针对性的问答和代码建议。下面我们将分别介绍在编程工作中,如何关联本地文件,并且使用不同的指令,来获得更精准的分析…

100%国产化网卡,联瑞在行动

国产CPU与国产网卡都是我国信息技术产业中的重要组成部分,它们的发展对于提升我国信息技术水平、保障信息安全和促进经济发展具有重要意义。 国产CPU在近年来取得了显著进展。例如飞腾、申威、海光、华为鲲鹏、龙芯、兆芯等品牌的CPU产品百花齐放,这些产品在性能上已经能够与…

C++面向对象语言自制多级菜单

C++面向对象语言自制多级菜单,可移植进嵌入式系统因为要做一个小应用,需要一个菜单类,在网上找了许久,也没有找到一款心仪的菜单类,索性用C++语言,自制一个命令行级别的菜单类,并制作成库,现记录下来,供以后借鉴。 一、特性无限制条目 无限制层级 用户自定义条目和动作…