线程的生命周期和常用方法

线程的生命周期和常用方法生命周期根据 jdk 官方文档 线程状态有以下几种 NEW 尚未启动的线程处于此状态 RUNNABLE 在 Java 虚拟机中执行的线程处于此状态 BLOCKED 被阻塞等待监视器锁定的线程处于此状态 WAITING 正在等待另一个线程执行特定动作的

欢迎大家来到IT世界,在知识的湖畔探索吧!

生命周期

根据jdk官方文档,线程状态有以下几种

  • NEW 尚未启动的线程处于此状态。
  • RUNNABLE 在Java虚拟机中执行的线程处于此状态。
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态。
  • WAITING 正在等待另一个线程执行特定动作的线程处于此状态。
  • TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED 已退出的线程处于此状态。

一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

如图示所见

线程的生命周期和常用方法



欢迎大家来到IT世界,在知识的湖畔探索吧!

代码演示

NEW / TIMED_WAITING / TERMINATED

java复制代码package ThreadMethod; import java.util.concurrent.TimeUnit; public class ThreadState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); System.out.println("线程状态:"+ thread.getState()); thread.start(); Thread.sleep(2000); System.out.println("线程状态:" + thread.getState()); Thread.sleep(2000); System.out.println("线程状态:" + thread.getState()); } } 

欢迎大家来到IT世界,在知识的湖畔探索吧!

结果

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码线程状态:NEW 线程状态:TIMED_WAITING 线程状态:TERMINATED 线程状态:TERMINATED 

WAITING / BLOCKED

java复制代码package ThreadMethod; public class BlockThreadState implements Runnable { static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { BlockThreadState blockThread = new BlockThreadState(); Thread thread = new Thread(blockThread); System.out.println("thread state : " + thread.getState()); thread.start(); Thread.sleep(20); System.out.println("thread state : " + thread.getState()); synchronized (lock){ lock.notify(); } System.out.println("thread state : " + thread.getState()); Thread.sleep(20); System.out.println("thread state : " + thread.getState()); } @Override public void run() { try { synchronized (lock){ // wait状态 lock.wait(); // synchronized重新拿到锁 处于block状态 for (int i = 0; i < ; i++) { continue; } } } catch (InterruptedException e) { e.printStackTrace(); } } } 

结果

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码thread state : NEW thread state : WAITING thread state : BLOCKED thread state : TERMINATED 

常见方法

wait()

wait()方法执行后,会阻塞线程,同时释放锁,如果想要唤醒该线程,则需要以下条件

  • 另一个线程调用这个对象的notify()方法,且刚好被唤醒的时本线程
  • 另一个线程调用了这个对象的notifyAll()方法
  • 过了wait(long timeout) 规定的超时时间,如果传入0就是永久等待
  • 线程自身调用了interrupt()

PS: 使用wait()方法时,必须先拥有monitor锁, 也就是说wait方法需要放在同步代码块中执行

notify/notifyAll

notify/notifyAll用于唤醒线程,当另一个线程调用wait()进入 waitting状态时,另一个线程调用notifyAll()可唤醒当前线程(如果有多个线程,使用notify并不一定能够唤醒线程)

组合使用示例

java复制代码public class BlockThread { public static void main(String[] args) { Message message = new Message(); // 创建一个等待线程 Thread waitThread = new Thread(new WaitThread(message)); // 创建一个唤醒线程 Thread notifyThread = new Thread(new NotifyThread(message)); // 启动等待线程和唤醒线程 waitThread.start(); notifyThread.start(); } } // 共享的消息类 class Message { private boolean isReady = false; // 等待方法 public synchronized void waitForMessage() { while (!isReady) { try { // 当消息不可用时,线程进入等待状态 System.out.println("线程进入等待状态"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 执行到这里表示收到消息,进行处理 System.out.println("收到消息!"); } // 唤醒方法 public synchronized void sendMessage() { // 做一些准备工作,例如获取消息 // 唤醒等待的线程 isReady = true; System.out.println("唤醒等待的线程"); notify(); } } // 等待线程 class WaitThread implements Runnable { private Message message; public WaitThread(Message message) { this.message = message; } @Override public void run() { // 等待消息 message.waitForMessage(); } } // 唤醒线程 class NotifyThread implements Runnable { private Message message; public NotifyThread(Message message) { this.message = message; } @Override public void run() { // 发送消息 message.sendMessage(); } } /* 线程进入等待状态 唤醒等待的线程 收到消息! Process finished with exit code 0 */ 

图示 monitor锁

线程的生命周期和常用方法

  • Entry Set 入口集 线程进入后抢锁
  • The owner 锁持有线程 如果方法没有执行完之前没有释放锁,则程序正常退出并释放锁 如果方法没有执行完之前释放了锁如调用了wait()方法,则程序再次进入等待集进行抢锁
  • Wait Set 等待集 如果上方入口集,不同点是等待集在执行方法锁时中途释放了锁

wait、notify 实现生产者消费者模式

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码import java.util.LinkedList; import java.util.List; import java.util.TreeMap; public class ProductAndConsumer { List<Object> container = new LinkedList<>(); public static void main(String[] args) { ProductAndConsumer productAndConsumer = new ProductAndConsumer(); Thread thread1 = new Thread(new Product(productAndConsumer)); Thread thread2 = new Thread(new Consumer(productAndConsumer)); thread1.start(); thread2.start(); } public synchronized void addObject() throws InterruptedException { if (container.size() >=100){ wait(); } container.add(new Object()); System.out.println("正在生产第"+ container.size() + "个"); notify(); } public synchronized void conObject() throws InterruptedException { if (container.size() == 0){ wait(); } container.remove(0); System.out.println("正在消费第"+ container.size() + "个"); notify(); } } class Product implements Runnable{ private final ProductAndConsumer productAndConsumer; Product(ProductAndConsumer productAndConsumer){ this.productAndConsumer = productAndConsumer; } @Override public void run() { while (true){ try { productAndConsumer.addObject(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer implements Runnable{ private final ProductAndConsumer productAndConsumer; Consumer(ProductAndConsumer productAndConsumer){ this.productAndConsumer = productAndConsumer; } @Override public void run() { while (true){ try { productAndConsumer.conObject(); } catch (InterruptedException e) { e.printStackTrace(); } } } } 

解释

  • 使用类ProductAndConsumer作为锁对象,container作为容器
  • wait notify来进行线程之间的通讯,在数量满足条件时使用wait释放当前锁对象,另一个对象拿到锁之后进行生产或消费

图示

线程的生命周期和常用方法

sleep()

Thread.sleep()是Java中一个静态native方法,用于使当前线程进入休眠状态(暂停执行)一段指定的时间。

它的方法签名为:

java复制代码public static native void sleep(long millis) throws InterruptedException; 

参数millis表示线程休眠的时间,以毫秒为单位。传入的值是一个正整数,表示线程要休眠的毫秒数。注意,该方法会抛出InterruptedException异常,因为线程在休眠期间可能被其他线程中断。

特点和用法

  • 线程阻塞:调用Thread.sleep()方法会导致当前线程暂停执行,进入阻塞状态。在指定的时间内,线程不会进行任何操作。
  • 时间精度:传入的休眠时间是以毫秒为单位,但实际的休眠时间可能会稍长或稍短。具体的精度取决于底层操作系统和JVM的实现。
  • 中断响应:如果在线程休眠期间,另一个线程中断了正在休眠的线程,Thread.sleep()方法会抛出InterruptedException异常。可以在catch块中处理该异常,或者将异常继续向上抛出。
  • 不会释放锁:Thread.sleep()方法会暂停当前线程的执行,但不会释放任何锁。如果线程在执行同步代码块或同步方法时调用了Thread.sleep(),其他线程仍无法获得该锁。
  • 静态方法:Thread.sleep()是一个静态方法,可以直接通过Thread类调用,无需创建线程对象。
  • 用途:常见的用途包括模拟延迟、定时任务、控制线程执行顺序等。

Thread.sleep和TimeUnit比较

  • 精度和可读性: Thread.sleep()的参数是以毫秒为单位的时间值,表示线程要休眠的时间。而TimeUnit提供了更高层次的时间单位,如TimeUnit.SECONDS表示秒,TimeUnit.MILLISECONDS表示毫秒等。使用TimeUnit可以使代码更具可读性,而不需要手动计算毫秒数。
  • 异常处理: Thread.sleep()方法会抛出InterruptedException异常,因为线程在休眠期间可能会被其他线程中断。而TimeUnit方式不会直接抛出异常,需要开发者手动处理中断情况。
  • 静态与非静态: Thread.sleep()是Thread类的静态方法,可以直接通过类名调用。而TimeUnit是一个枚举类,需要通过具体的枚举常量来调用其方法,例如TimeUnit.SECONDS.sleep(1)。
  • 可读性和易用性: 使用TimeUnit可以提高代码的可读性,因为可以直观地表示时间单位。此外,TimeUnit还提供了其他方法,如TimeUnit.toMillis()、TimeUnit.toSeconds()等,方便进行时间单位之间的转换。

TimeUnit源码

以下是截取部分源码

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码public enum TimeUnit { / * Performs a {@link Thread#sleep(long, int) Thread.sleep} using * this time unit. * This is a convenience method that converts time arguments into the * form required by the {@code Thread.sleep} method. * * @param timeout the minimum time to sleep. If less than * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping */ public void sleep(long timeout) throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); Thread.sleep(ms, ns); } } } 

可以看到TimeUnit底层还是调用了Thread.sleep() 有一个比较隐含的地方就是当使用过TimeUnit的sleep方法时,如果传入的时间小于是不会进入if判断,而Thread.sleep()方法如果传参小于0则会抛出异常(源码见下)

java复制代码public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > ) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >=  || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } 

join()

Thread.join()是Java中的一个方法,用于等待调用该方法的线程执行完毕。它的作用是让当前线程等待指定线程执行结束,然后再继续执行当前线程的后续代码。

简单来说就是阻塞主线程。

简单示例

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码package ThreadMethod; public class JoinMethod { public static void main(String[] args) { Thread thread1 = new Thread(()->{ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程执行完毕"); },"子线程"); thread1.start(); try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程执行完毕"); } } 

结果

java复制代码子线程执行完毕 主线程执行完毕 

可以看到子线程等待了3秒,但是最终还是子线程先执行完毕在执行主线程打印,原因是因为thread1.join 对主线程进行了阻塞,这是主线程需要等子线程执行完毕才会执行后面的语句

特点和注意事项

  • 等待执行: 调用join()方法的线程将会等待指定线程执行完毕。如果指定线程已经执行完毕,则join()方法会立即返回。
  • 阻塞调用线程: 在调用join()方法期间,当前线程将会被阻塞,暂停执行。只有当指定线程执行完毕后,当前线程才会解除阻塞,继续执行。
  • 异常处理: join()方法会抛出InterruptedException异常,因为在等待过程中,当前线程可能会被中断。可以在catch块中处理该异常,或将异常继续向上抛出。
  • 顺序执行: 通过使用join()方法,可以控制线程的执行顺序。调用join()方法后,当前线程会等待指定线程执行完毕,然后再继续执行后续代码。
  • 调用对象: join()方法是一个实例方法,需要通过线程对象调用。例如,如果thread1是一个Thread对象,可以使用thread1.join()来等待thread1执行完毕。

源码和底层实现

以下截取部分Thread.join源码

欢迎大家来到IT世界,在知识的湖畔探索吧!java复制代码// 入口 public final void join() throws InterruptedException { join(0); } // 调用的join(0) public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } // 实际调用的本地native方法 wait public final native void wait(long timeout) throws InterruptedException; 

可以看到join的底层还是使用wait方法实现的,子线程调用wait方法让主线程进入等待状态,在运行结束后自动调用notify方法唤醒主线程。(具体唤醒的方法在jvm中)

有一点疑问我没有找到解释:为什么调用子线程的wait(0)方法,阻塞的确是主线程呢?

CountDownLatch和CyclicBarrier

使用countDownLatch和CyclicBarrier也可以实现线程之间的阻塞,具体暂不讨论

yeild() (让步)

yield() 是一个静态方法,它属于 Thread 类,用于提示调度器将当前线程让出 CPU 的执行权,使得其他具有相同优先级的线程有机会执行。

yield() 方法的调用并不能保证一定会使其他线程获得执行机会,它仅是一个提示。具体的调度行为取决于操作系统和 JVM 的实现。因此,在实际应用中,不应过度依赖 yield() 方法来控制线程的执行顺序,而应使用更可靠的线程同步机制来实现需要的线程协作和同步。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/136415.html

(0)
上一篇 48分钟前
下一篇 38分钟前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信