Thread

Thread类的UML图
Thread类的UML图

创建线程的方式

 

方法一 直接使用Thread

public class MyThread extends Thread { //定义指定线程名称的构造方法 public MyThread(String name) { //调用父类的String参数的构造方法,指定线程的名称 super(name); } /** * 重写run方法,完成该线程执行的逻辑 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+":正在执行!"+i); } } } //可以使用匿名内部类或者lambda进行简化

方法二 实现Runnable接口

源码 :
分配一个新的Thread对象。 此构造函数与Thread (null, target, gname)具有相同的效果,其中gname是新生成的名称。 自动生成的名称格式为"Thread-"+ n ,其中n是一个整数。 参数:target – 在该线程启动时调用其run方法的对象。 如果为null ,则该类的run方法不执行任何操作。
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, target, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. * * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this classes {@code run} method does * nothing. */ public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
测试
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } } public class Demo { public static void main(String[] args) { //创建自定义类对象 线程任务对象 MyRunnable mr = new MyRunnable(); //创建线程对象 Thread t = new Thread(mr, "小强"); t.start(); for (int i = 0; i < 20; i++) { System.out.println("旺财 " + i); } } }

方法一和方法二的异

源码如下
相同点
两者都是调用了Thread的run()方法,
不同点
方法二在初始化线程对象的时候会给Thread的target成员变量赋值,最终执行Thread的run()方法,会调用runnable对象的run()方法
方法1 是把线程和任务合并在了一起 耦合度更高,方法2 是把线程和任务分开了,耦合度更低,实际上使用了静态代理的设计模式
代理模式
代理模式
 
更推荐方法二,原因如下
  1. 用 Runnable 更容易与线程池等高级 API 配合
  1. 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活
 

FutureTask配合Thread

//创建任务对象 FutureTask<Integer> futureTask = new FutureTask(() -> { log.debug("hello"); return 100; }); new Thread(futureTask, "t3").start(); //主线程阻塞,同步等待task执行完毕的结果 Integer result = futureTask.get(); log.debug("结果是:{}",result);

线程运行的原理

栈与栈帧

Java Virtual Machine Stacks (Java 虚拟机栈)
我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
public class TestFrames { public static void main(String[] args) { Thread t1 = new Thread(){ @Override public void run() { method1(20); } }; t1.setName("t1"); t1.start(); method1(10); } private static void method1(int x) { int y = x + 1; Object m = method2(); System.out.println(m); } private static Object method2() { Object n = new Object(); return n; } } Thread t1 = new Thread(() -> { log.debug(Thread.currentThread().getName()); FileReader.read(Constants.MP4_FULL_PATH); }, "t1"); t1.run(); log.debug("do other things"); //output //19:39:14 [main] c.TestStart - main //19:39:14 [main] c.FileReader - read [1.mp4] start ... //19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms 19:39:18 [main] c.TestStart - do other things ...
notion image

线程上下文切换

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码
  • 线程的 cpu 时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等Context Switch 频繁发生会影响性能

线程常用方法

notion image

start与run

start() 源码
使该线程开始执行; Java虚拟机调用这个线程的run方法。 结果是两个线程并发运行:当前线程(从调用start方法返回)和另一个线程(执行其run方法)。多次启动一个线程是不合法的。 特别是,线程一旦完成执行就可能不会重新启动。如果线程已经启动抛出:IllegalThreadStateException
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ //判断线程是不是NEW状态 if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ //添加到线程组 group.add(this); boolean started = false; try { //调用操作系统的方法创建线程 start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0();
java线程是通过start的方法启动执行的,主要内容在native方法start0中,Openjdk的写JNI一般是一一对应的,Thread.java对应的就是openjdk8\jdk\src\share\native\java\lang下的Thread.c
start0其实就是JVM_StartThread。此时查看下图的源代码可以看到在jvm.h中找到了声明,openjdk8\hotspot\src\share\vm\prims 目录下的jvm.cpp中有实现。
notion image
notion image
notion image
                                                                        openjdk8\hotspot\src\share\vm\runtime 的thread.cpp
openjdk8\hotspot\src\share\vm\runtime 的thread.cpp
notion image
run()方法源码
如果该线程是使用单独的Runnable运行对象构造的,则调用该Runnable对象的run()方法; 否则,此方法不执行任何操作并返回。 Thread子类应该覆盖这个方法。
/** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }
调用run
Thread t1 = new Thread(() -> { log.debug(Thread.currentThread().getName()); FileReader.read(Constants.MP4_FULL_PATH); }, "t1"); t1.run(); log.debug("do other things"); //output //19:39:14 [main] c.TestStart - main //19:39:14 [main] c.FileReader - read [1.mp4] start ... //19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms 19:39:18 [main] c.TestStart - do other things ...
程序仍在 main 线程运行, FileReader.read() 方法调用还是同步的
调用 start
将上述代码的 t1.run() 改为 t1.start();
输出
//19:41:30 [main] c.TestStart - do other things ... //19:41:30 [t1] c.TestStart - t1 //19:41:30 [t1] c.FileReader - read [1.mp4] start ... //19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms
程序在 t1 线程运行, FileReader.read() 方法调用是异步的
总结
  • 直接调用 run 是在主线程中执行了 run,没有启动新的线程
  • 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

sleep 与 yield

sleep

/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. *使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。该线程不会失去任何监视器的所有权。 */ public static native void sleep(long millis) throws InterruptedException;
  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  1. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  1. 睡眠结束后的线程未必会立刻得到执行
  1. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
@Slf4j(topic = "c.Test6") public class Test6 { public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); log.debug("t1 state: {}", t1.getState()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("t1 state: {}", t1.getState()); } } //output //15:49:39.196 c.Test6 [main] - t1 state: RUNNABLE //15:49:39.718 c.Test6 [main] - t1 state: TIMED_WAITING
打断sleep状态的线程
@Slf4j(topic = "c.Test7") public class Test7 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("enter sleep..."); try { Thread.sleep(2000); } catch (InterruptedException e) { log.debug("wake up..."); e.printStackTrace(); } } }; t1.start(); Thread.sleep(1000); log.debug("interrupt..."); t1.interrupt(); } }
TimeUnit的使用
@Slf4j(topic = "c.Test8") public class Test8 { public static void main(String[] args) throws InterruptedException { log.debug("enter"); TimeUnit.SECONDS.sleep(1); log.debug("end"); // Thread.sleep(1000); } }

yield

向调度程序提示当前线程愿意放弃其当前对处理器的使用。 调度程序可以随意忽略此提示。 Yield 是一种启发式尝试,旨在改善线程之间的相对进展,否则会过度使用 CPU。 它的使用应与详细的分析和基准测试相结合,以确保它确实具有预期的效果。 很少适合使用这种方法。 它对于调试或测试目的可能很有用,它可能有助于重现由于竞争条件引起的错误。 在设计并发控制结构(例如java.util.concurrent.locks包中的结构)时,它也可能很有用。
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();
  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  1. 具体的实现依赖于操作系统的任务调度器
@Slf4j(topic = "c.TestYield") public class TestYield { public static void main(String[] args) { Runnable task1 = () -> { int count = 0; for (;;) { System.out.println("---->1 " + count++); } }; Runnable task2 = () -> { int count = 0; for (;;) { Thread.yield(); System.out.println(" ---->2 " + count++); } }; Thread t1 = new Thread(task1, "t1"); Thread t2 = new Thread(task2, "t2"); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); } }//t1线程调度的频率较高
调用sleep之后线程变为阻塞状态不可获得cpu时间片,调用yield之后依然有机会获得cpu时间片

设置线程的优先级(setPriority)

线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
Thread源码
public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } /** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
验证优先级
@Slf4j(topic = "c.Test9") public class Test9 { public static void main(String[] args) { Runnable task1 = () -> { int count = 0; for (;;) { System.out.println("---->1 " + count++); } }; Runnable task2 = () -> { int count = 0; for (;;) { System.out.println(" ---->2 " + count++); } }; Thread t1 = new Thread(task1, "t1"); Thread t2 = new Thread(task2, "t2"); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); } } //t2获得cpu时间片较多,调度频率更高

join

能够使当前执行的线程停下来等待,直至调用join方法的那个线程结束,再恢复执行。例如如果有一个线程A正在运行,用户希望插入一个线程B,并且要求线程B执行完毕,然后再继续线程A,此时可以使用join()方法来完成这个需求。
@Slf4j(topic = "c.Test10") public class Test10 { static int r = 0; public static void main(String[] args) throws InterruptedException { test1(); } private static void test1() throws InterruptedException { log.debug("开始"); Thread t1 = new Thread(() -> { log.debug("开始"); sleep(1); log.debug("结束"); r = 10; },"t1"); t1.start(); // t1.join(); 不执行此行代码最终打印r=0.执行后r=10 log.debug("结果为:{}", r); log.debug("结束"); } }
join(t) 表示最多阻塞t 毫秒, 实际上的阻塞时间可以小于t 毫秒
@Slf4j(topic = "c.TestJoin") public class TestJoin { static int r = 0; static int r1 = 0; static int r2 = 0; public static void main(String[] args) throws InterruptedException { test3(); } public static void test3() throws InterruptedException { Thread t1 = new Thread(() -> { sleep(2); r1 = 10; }); long start = System.currentTimeMillis(); t1.start(); // 线程执行结束会导致 join 结束 log.debug("join begin"); t1.join(3000);// t1线程2秒就运行结束了,所以只会阻塞2秒 long end = System.currentTimeMillis(); log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start); //output //20:12:30.096 c.TestJoin [main] - join begin //20:12:32.096 c.TestJoin [main] - r1: 10 r2: 0 cost: 2001 } private static void test1() throws InterruptedException { log.debug("开始"); Thread t1 = new Thread(() -> { log.debug("开始"); sleep(1); log.debug("结束"); r = 10; }); t1.start(); t1.join(); log.debug("结果为:{}", r); log.debug("结束"); } //output //20:14:57.897 c.TestJoin [main] - 开始 //20:14:57.967 c.TestJoin [Thread-0] - 开始 //20:14:58.968 c.TestJoin [Thread-0] - 结束 //20:14:58.968 c.TestJoin [main] - 结果为:10 //20:14:58.969 c.TestJoin [main] - 结束 }
两个线程分别join,互不影响
public static void main(String[] args) throws InterruptedException { test2(); } private static void test2() throws InterruptedException { Thread t1 = new Thread(() -> { sleep(1); r1 = 10; }); Thread t2 = new Thread(() -> { sleep(2); r2 = 20; }); t1.start(); t2.start(); long start = System.currentTimeMillis(); log.debug("join begin"); t2.join(); log.debug("t2 join end"); t1.join(); log.debug("t1 join end"); long end = System.currentTimeMillis(); log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start); //output: //20:14:16.465 c.TestJoin [main] - join begin //20:14:18.467 c.TestJoin [main] - t2 join end //20:14:18.467 c.TestJoin [main] - t1 join end //20:14:18.467 c.TestJoin [main] - r1: 10 r2: 20 cost: 2004 }

interrupt

源码
/*中断此线程。 如果此线程在调用Object类的wait() 、 wait(long)或wait(long, int)方法或join() 、 join(long) 、 join(long, int)被阻塞、 sleep(long)或sleep(long, int) ,此类的方法,则其中断状态将被清除并收到InterruptedException 。 如果此线程在InterruptibleChannel的 I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,并且线程将收到java.nio.channels.ClosedByInterruptException 。 如果此线程在java.nio.channels.Selector被阻塞,则该线程的中断状态将被设置,并且它将立即从选择操作中返回,可能具有非零值,就像调用了选择器的wakeup方法一样。 如果前面的条件都不成立,则将设置此线程的中断状态。 中断一个不活跃的线程不需要有任何影响。 */ public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); }

打断sleep,wait,join的线程

这几个方法都会让线程进入阻塞状态,打断sleep的线程,会清空打断状态,并抛出异常终止当前线程的运行
@Slf4j(topic = "c.Test11") public class Test11 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("sleep..."); try { Thread.sleep(5000); // wait, join } catch (InterruptedException e) { e.printStackTrace(); } },"t1"); t1.start(); Thread.sleep(1000); log.debug("interrupt"); t1.interrupt(); log.debug("打断标记:{}", t1.isInterrupted()); } } //output 20:18:56.791 c.Test11 [t1] - sleep... 20:18:57.789 c.Test11 [main] - interrupt 20:18:57.791 c.Test11 [main] - 打断标记:false //sleep ,wait,join会清除打断标记 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at cn.itcast.test.Test11.lambda$main$0(Test11.java:12) at java.lang.Thread.run(Thread.java:748)

打断正常运行的线程

打断正常运行的线程,不会清空打断状态
@Slf4j(topic = "c.Test12") public class Test12 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while(true) { boolean interrupted = Thread.currentThread().isInterrupted(); if(interrupted) { log.debug("被打断了, 退出循环"); break; } } }, "t1"); t1.start(); Thread.sleep(1000); log.debug("interrupt"); t1.interrupt(); } 20:26:05.085 c.Test12 [main] - interrupt 20:26:05.093 c.Test12 [t1] - 被打断了, 退出循环

打断park线程

打断park线程,线程会继续执行,并且当打断状态为true时再次调用park方法会失效
private static void test3() throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("park..."); LockSupport.park(); log.debug("unpark..."); log.debug("打断状态:{}", Thread.currentThread().isInterrupted()); LockSupport.park(); log.debug("unpark..."); }, "t1"); t1.start(); sleep(1); t1.interrupt(); } // output // 21:36:29.443 c.Test14 [t1] - park... // 21:36:30.441 c.Test14 [t1] - unpark... // 21:36:30.441 c.Test14 [t1] - 打断状态:true // 21:36:30.442 c.Test14 [t1] - unpark...
private static void test4() { Thread t1 = new Thread(() -> { for (int i = 0; i < 5; i++) { log.debug("park..."); LockSupport.park(); log.debug("打断状态:{}", Thread.interrupted()); LockSupport.park(); log.debug("unpark..."); } }); t1.start(); sleep(1); t1.interrupt(); } // // 21:40:36.500 c.Test14 [Thread-0] - park... // 21:40:37.499 c.Test14 [Thread-0] - 打断状态:true
使用 Thread.interrupted()会清除打断状态

一些过时不推荐的方法

notion image

两阶段终止

具体实现见多线程设计模式

线程状态

五种状态(操作系统层面)

notion image
初始状态:仅是在语言层面创建了线程对象,还未与操作系统线程关联
可运行状态(就绪状态):指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行
运行状态:指获取了CPU时间片运行中的状态.当CPU时间片用完,会从运行状态转换至可运行状态,会导致线程的上下文切换
阻塞状态:
  • 如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用到CPU,会导致线程上下文切换,进入阻塞状态等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态
  • 与可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
终止状态:表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

六种状态(Java API层面)

根据Thread.State枚举,分为六种状态
notion image
  1. NEW: 线程刚被创建,但是还没有调用start()方法
  1. RUNNABLE: 当调用了start()方法之后,
    1. 注意,JAVA API层面的RUNNABLE状态涵盖了操作系统层面的可运行状态,运行状态和阻塞状态(由于BIO导致的线程阻塞,在java里无法区分,仍然认为是可运行)
      public class TestState2 { public static void main(String[] args) throws InterruptedException { new Thread(() -> { FileReader.read(Constants.MP4_FULL_PATH); FileReader.read(Constants.MP4_FULL_PATH); FileReader.read(Constants.MP4_FULL_PATH); }, "t1").start(); Thread.sleep(1000); System.out.println("ok"); } }
      notion image
  1. BOLOCKEDWAITINGTIMED_WAITING都是java API层面对阻塞状态的细分
  1. TERMINATED当线程代码运行结束
六种状态演示
@Slf4j(topic = "c.TestState") public class TestState { public static void main(String[] args) throws IOException { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("running..."); } }; Thread t2 = new Thread("t2") { @Override public void run() { while(true) { // runnable } } }; t2.start(); Thread t3 = new Thread("t3") { @Override public void run() { log.debug("running..."); } }; t3.start(); Thread t4 = new Thread("t4") { @Override public void run() { synchronized (TestState.class) { try { Thread.sleep(1000000); // timed_waiting } catch (InterruptedException e) { e.printStackTrace(); } } } }; t4.start(); Thread t5 = new Thread("t5") { @Override public void run() { try { t2.join(); // waiting } catch (InterruptedException e) { e.printStackTrace(); } } }; t5.start(); Thread t6 = new Thread("t6") { @Override public void run() { synchronized (TestState.class) { // blocked try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t6.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("t1 state {}", t1.getState()); log.debug("t2 state {}", t2.getState()); log.debug("t3 state {}", t3.getState()); log.debug("t4 state {}", t4.getState()); log.debug("t5 state {}", t5.getState()); log.debug("t6 state {}", t6.getState()); System.in.read(); } } //output //19:31:36.992 c.TestState [t3] - running... //19:31:37.479 c.TestState [main] - t1 state NEW //19:31:37.497 c.TestState [main] - t2 state RUNNABLE //19:31:37.497 c.TestState [main] - t3 state TERMINATED //19:31:37.497 c.TestState [main] - t4 state TIMED_WAITING //19:31:37.497 c.TestState [main] - t5 state WAITING //19:31:37.497 c.TestState [main] - t6 state BLOCKED
java源码java.lang.Thread.State对6种状态的解释
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
 

线程状态转换

notion image
假设有线程 Thread t

NEW --> RUNNABLE

当调用 t.start() 方法时,由 NEW --> RUNNABLE

RUNNABLE <--> WAITING

t 线程用 synchronized(obj) 获取了对象锁
  • 调用 obj.wait() 方法时,t 线程从 RUNNABLE --> WAITING
  • 调用 obj.notify()obj.notifyAll()t.interrupt()
    • 竞争锁成功,t 线程从 WAITING --> RUNNABLE
    • 竞争锁失败,t 线程从 WAITING --> BLOCKED
    •  
       
t.join()
  • 当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING
    • 注意是当前线程在t 线程对象的监视器上等待
  • t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE
 
当前线程调用 LockSupport.park()
  • 当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING
  • 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING --> RUNNABLE
 
 

RUNNABLE <--> BLOCKED

  • t 线程用 synchronized(obj) 获取对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED
  • 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争 成功,从 BLOCKED --> RUNNABLE ,其它失败的线程仍然 BLOCKED
 

RUNNABLE <--> TERMINATED

当前线程所有代码运行完毕,进入 TERMINATED
notion image