JavaEE 初阶篇-深入了解多线程等待与多线程状态
🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
1.0 线程等待
1.1 线程等待 - join() 方法
1.1.1 main 线程中等待多个线程
1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程
1.1.3 其他线程阻塞等待 main 线程
1.1.4 在指定的时间内阻塞等待
1.2 线程等待 - Thread.sleep() 方法
2.0 线程状态
2.1 新建状态 - NEW
2.2 就绪状态 - Runnable
2.3 终止状态 -Terminated
2.4 等待状态 - Waiting
2.5 超时等待状态 - Time_Waiting
2.6 阻塞状态 - Blocked
2.7 线程状态之间的相互转换图
1.0 线程等待
在线程编程中,线程等待是指一个线程暂停执行,直到某个条件满足或者其他线程执行完毕后再继续执行。线程等待的方法:join() 方法、Thread.sleep() 方法等等
1.1 线程等待 - join() 方法
join() 方法是 Thread 类的一个方法,用于让一个线程等待另一个线程执行完毕。当在一个线程对象上调用 join() 方法时,当前线程会被阻塞,直到被调用的线程执行完毕。
具体来说,调用 thread.join() 会使当前线程等待 thread 线程执行完毕。如果 thread 线程已经执行完毕,那么 join() 方法会立即返回;如果 thread 线程还在执行,当前线程会被阻塞,直到 thread 线程执行完毕。
代码如下:
public class demo1 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; imain 线程调用 thread.join() ,就会阻塞 main 线程继续执行,会让 thread 线程执行完毕之后,main 线程解除阻塞,继续执行下去。
运行结果:
补充: main 中调用 join() 方法,有以下可能性:
1.0 如果 t 线程此时已经结束了,此时 join() 方法就会立即返回。
2.0 如果 t 线程此时还没有结束,此时 main 就会阻塞等待,一直等待到 t 线程结束之后,main 线程才会接触阻塞,继续执行。
3.0 如果调用等待阻塞的线程对象还没创建 pcb 的时候(即还没 start() 的时候),那么调用 join() 方法的线程会直接解除阻塞。
1.1.1 main 线程中等待多个线程
在 main 线程中多次调用 join() 方法时,先执行 t1.join(),如果 t1 还没结束,main 继续阻塞等待,t1 结束之后,继续执行 t2.join() 方法,再等待 t2 结束。注意,t1 与 t2 之间同样是抢占式执行随机调度,所以先后顺序对于 main 线程来说没有什么区别。
代码如下:
public class demo2 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ for (int i = 0; i { for (int i = 0; i运行结果:
1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程
这种情况是按顺序执行的,大致就是串行执行一样。顺序为:先要执行完 t1 再执行 t2 最后再执行 main 线程。
代码如下:
public class demo3 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ for (int i = 0; i { try { t1.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } for (int i = 0; it1.start() 立即创建 t1 线程,再 t2.start() 创建线程,t1 没有任何阻塞就会直接执行代码,而 t2 遇到了阻塞,需要等待 t1 执行完毕之后,t2 才会解除阻塞,同时由于 main 线程阻塞了,需要等待 t2 执行完毕,当 t2 执行完毕之后,main 线程解除阻塞了,执行 main 线程中的代码。
运行结果:
1.1.3 其他线程阻塞等待 main 线程
t1 线程阻塞等待 main 线程执行完毕之后,再执行 t1 线程。
代码如下:
public class demo4 { public static void main(String[] args) throws InterruptedException { //拿到当前 main 线程对象 Thread mainThread = Thread.currentThread(); Thread t1 = new Thread(()->{ //在 t1 线程中调用 join() 方法, //阻塞当前 t1 线程,等待 main 线程执行完毕 try { mainThread.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } for (int i = 0; i运行结果:
1.1.4 在指定的时间内阻塞等待
join() 方法还有一个重载的版本,可以指定一个超时时间,即 join(long millis),表示当前线程最多等待 millis 毫秒,如果超过这个时间 thread 线程还没有执行完毕,当前线程会继续往下执行。简单来说,即使 thread 这个线程还没有结束,main 线程都不会继续等待了;如果 thread 在规定的时间内提前结束,那么 main 也会提前解除阻塞。
代码如下:
public class demo5 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i运行结果:
1.2 线程等待 - Thread.sleep() 方法
是一个静态方法,它使当前线程暂停执行一段时间。该方法接受一个以毫秒为单位的时间参数,指定线程暂停的时间长度。在这段时间内,线程不会执行任何操作,但是线程的状态仍然是 Runnable 状态,可以随时被调度器调度执行。
需要注意的是,Thread.sleep() 方法不是真正意义上的线程等待,它只是让线程暂停执行一段时间,不会释放锁或资源。在实际开发中,应根据具体需求选择合适的线程等待机制,以确保程序的正确性和效率。
代码如下:
public class demo6 { public static void main(String[] args) { Thread thread = new Thread(()->{ for (int i = 0; i运行结果:
每相隔 1 秒就会输出一次。
需要注意的是,这里的 Thread.sleep() 方法是受查异常,且 run() 是重写父类的方法,该 run() 方法的方法名、参数列表、声明异常都需要与父类保持一致,因此这里不能声明异常,只能捕获异常处理。
2.0 线程状态
在 Java 中主要包括六种不同的状态。
2.1 新建状态 - NEW
当创建一个线程对象时,线程处于新建状态,此时线程对象已经创建好了,但是还没调用 start() 方法启动线程,因此线程还没被创建出来。
2.2 就绪状态 - Runnable
有两种情况都属于就绪状态:1)还没运行,就绪状态。但是线程已经准备好运行了,只等待被 CPU 调度执行。2)线程正在被 CPU 调度执行中,运行状态。总而言之,无论是就绪状态还是运行状态在 Java 中都属于 Runnable 状态,即就绪状态。
2.3 终止状态 -Terminated
线程执行完任务后或者出现异常导致线程终止时,线程进入终止状态。在终止状态下,线程不再执行任务。
2.4 等待状态 - Waiting
线程进入等待状态通常时因为调用了 thread.join() 方法等等。
代码如下:
public class demo7 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ while (true){ System.out.println(1); } }); thread.start(); thread.join(); System.out.println("正在执行 main 线程"); } }演示线程等待:
main 线程调用了 thread.join() 方法阻塞等待 thread 线程,又因为 thread 还当前为止还没结束, 所以当前 main 线程被阻塞了,因此 main 状态为 Waiting 状态。对于 thread 线程来说,目前的状态为 Runnable 状态。
2.5 超时等待状态 - Time_Waiting
线程调用带有超时参数的等待方法,比如 Thread.sleep(long millis) 等待方法。线程会进入超时等待状态下,线程会等待一段时间后自动恢复到就绪状态。
代码如下:
public class demo8 { public static void main(String[] args) { Thread thread = new Thread(()->{ try { Thread.sleep(9000000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("正在执行 thread 线程"); }); thread.start(); System.out.println("正在执行 main 线程"); } }演示超时等待:
2.6 阻塞状态 - Blocked
线程在特定情况下,会进入阻塞状态,比如调用了 Thread.sleep() 方法或者加锁。在线程阻塞状态下,线程暂时停止执行,直到满足特定条件后,才能继续执行。
代码如下:
死锁状态:两个线程两把锁
public class demo10 { public static void main(String[] args) { Object o1 = new Object(); Object o2 = new Object(); Thread t1 = new Thread(()->{ synchronized (o1){ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (o2){ System.out.println("正在执行 t1 线程"); } } }); Thread t2 = new Thread(()->{ synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (o1){ System.out.println("正在执行 t2 线程"); } } }); t1.start(); t2.start(); } }演示阻塞状态:
2.7 线程状态之间的相互转换图