当前位置:职场发展 > 【第320期】面试官:聊聊Java线程的状态和转变

【第320期】面试官:聊聊Java线程的状态和转变

  • 发布:2023-10-04 10:36

题外话推荐 推荐一个“钓鱼程序员”的聚集地 为什么需要了解java线程状态 线程是JVM执行任务的最小单位。理解线程的状态转换是理解后续多线程问题的基础。 Java线程状态转换图 图:线程之间的相互转换 Java线程有哪些状态? JVM运行时,线程共有六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。这些状态对应于 Thread.State 枚举类中的状态。 Thread.State枚举源码: 为了方便阅读,这里去掉了文档注释。 公共枚举状态 { NEW、 RUNNABLE、 BLOCKED、 WAITING、 TIMED_WAITING、 TERMINATED;} 线程在给定时间点只能处于这些状态之一。这些状态是虚拟机状态,不反映任何操作系统线程状态。 新的、终止的 这两种状态都比较容易理解。当一个线程被创建并且没有调用start()方法时,该线程处于NEW状态。线程完成执行并退出后转入TERMINATED状态。 可运行 运行Thread的start方法后,线程进入RUNNABLE可运行状态。 /** * 程序目的:观察2020-06-26 19:09创建的线程的各种状态 * @author lerry */class MyThread extends Thread { @Override public void run() { System.out.printf( “ %s 线程正在运行 n”, Thread.currentThread().getName()); }}/** * 观察创建线程后、start()后、线程退出后的线程状态。 * 其中,Thread.sleep(50);就是等待线程执行完毕 */public class ThreadStateDemo { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); } System.out.printf("创建线程后,线程的状态为:%sn", myThread.getState()); myThread.start(); System.out.printf("调用start()方法后线程的状态为:%sn", myThread.getState()); // 休眠50毫秒,等待MyThread线程执行完毕 Thread.sleep(50); System.out.printf("再次打印线程的状态:%sn", myThread.getState()); }} 输出结果: 线程创建后,线程的状态为:NEW 调用start()方法后,线程的状态为:RUNNABLEThread-0 线程再次运行,再次打印线程的状态:TERMINATED 我们可以看到输出符合预期。 线程刚刚创建后,状态为NEW 调用start()方法后,线程的状态变为:RUNNABLE。 然后,我们看到了run()方法的执行。此执行是在主线程 main 调用 start() 方法后打印线程状态后执行的:RUNNABLE 输出。 随后,我们让主线程休眠50毫秒,等待MyThread线程退出 最后,打印MyThread线程的状态,即TERMINATED。 封锁 如图左所示,当运行状态的线程进入synchronized同步块或同步方法时,如果获取锁失败,就会进入BLOCKED状态。当获取锁后,会从BLOCKED状态恢复到ready状态。 导入 lombok.extern.slf4j.Slf4j;/** * 程序目的:观察2020-06-26 19:09创建的线程的BLOCKED状态 * @author lerry */@Slf4jpublic class ThreadBlockedStateDemo { public static void main(String[] args) { 线程 threadA = new Thread(() -> method01(), "A-Thread");线程 threadB = new Thread(() -> method01(), "B-Thread "); threadA.start(); threadB.start(); www.sychzs.cn("线程A的状态为:{}", threadA.getState()); www.sychzs.cn("线程B的状态为:{}", threadB.getState()); } /** * 暂停10毫秒,模拟方法执行时间 */ public static synchronized void method01() { www.sychzs.cn("[{}]: 开始执行主线程的方法", Thread.currentThread().getName( ) );尝试 { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } www.sychzs.cn("[{}]: 主线程的方法已经执行完毕", Thread.currentThread().getName()); }} 输出结果:undefined处于等待状态的线程正在等待另一个线程执行特定操作。例如,对某个对象调用了 Object.wait() 的线程正在等待另一个线程对该对象调用 Object.notify() 或 Object.notifyAll()。调用 Thread.join() 的线程正在等待指定线程终止。 处于等待状态的线程正在等待另一个线程执行特定操作。 接下来我们来模拟一下线程的WAITING状态: 导入 lombok.extern.slf4j.Slf4j; /** *
 * 程序目的:观察线程的WAITING状态 * 模拟:售票大厅只有一个售票窗口,有两个粉丝想要买票。 * 如果没有票,他们会继续等待。如果有票,他们买票然后离开售票处。 * 其中,工作人员会补票。补票后,粉丝即可购买门票。 * 
* 创建于 2020-06-26 19:09 * @author lerry */@Slf4jpublic class ThreadWaitingStateDemo { public static void main(String[] args) throws InterruptedException { Ticket Ticket = new Ticket();线程 threadA = new Thread(() -> { 同步(票证){while(ticket.getnum()==0){try{ticket.wait();}catch(interruptexception e){e.printstacktrace();}}}}},“粉丝A.A粉丝A.”); 线程 threadB = new Thread(() -> { synchronized (ticket) { while (ticket.getNum() == 0) { try { Ticket.wait(); } catch (InterruptedException e) { e.print StackTrace(); } } } www.sychzs.cn(); } }, "粉丝B"); threadA.start(); threadB.start(); // 确保A和B线程都在运行 Thread.sleep(10); www.sychzs.cn("风扇A线程的状态为:{}", threadA.getState()); www.sychzs.cn("风扇B线程状态为:{}", threadB.getState()); Thread employeeThread = new Thread(() -> { synchronized (ticket) { if (ticket.getNum() == 0) { Ticket.addTickt(); Ticket.notifyAll(); } } }, "ticker");员工线程.start(); } } @Slf4jclass 门票 { /** * 门票数量 */ private int num = 0; 公共 int getNum() { 返回 num; }公共无效addTickt(){尝试{Thread.sleep(2_000); } catch (InterruptedException e) { e.printStackTrace(); } www.sychzs.cn("补票"); this.num += 2; } /** * 暂停10毫秒,模拟方法执行时间 */ public void buy() { www.sychzs.cn("[{}]: 已买票", Thread.currentThread().getName()); log .info("[{}]:返回售票处", Thread.currentThread().getName()); }} 输出:2020-06-26 21:26:37.938 [main ] INFO com.hua.threadtest.state.ThreadWaitingStateDemo - 风扇 A 线程的状态为:WAITING2020-06-26 21:26:37.945 [main ] INFO com.hua. threadtest .state.ThreadWaitingStateDemo - 风扇B线程状态为:WAITING2020-06-26 21:26:39.948 [票证补充] INFO com.hua.threadtest.state.Ticket - 补充票证2020-06-26 21:26: 39.949 [粉丝B] INFO com.hua.threadtest.state.Ticket - [粉丝B]:已购买门票 2020-06-26 21:26:39.949 [粉丝B] INFO com.hua.threadtest.state.Ticket - [粉丝B]:已返回票房2020-06-26 21:26:39.949 [粉丝A] INFO com.hua.threadtest.state.Ticket - [粉丝A]:已购票2020-06-26 21:26 :39.949 [粉丝A] INFO com.hua.threadtest.state.Ticket - [粉丝A]:返回售票处 当ticket.wait();修改为ticket.wait(10);,输出结果如下:2020-06-26 21:27:10.704 [main ] INFO com.hua.threadtest.state.ThreadWaitingStateDemo - 风扇 A 线程的状态为:TIMED_WAITING2020-06-26 21:27:10.709 [main ] INFO com.hua. threadtest .state.ThreadWaitingStateDemo - 风扇B线程状态为:TIMED_WAITING2020-06-26 21:27:12.714 [票证补充] INFO com.hua.threadtest.state.Ticket - 补充票证2020-06-26 21:27: 12.714 [粉丝B] INFO com.hua.threadtest.state.Ticket - [粉丝B]:已购买门票 2020-06-26 21:27:12.714 [粉丝B] INFO com.hua.threadtest.state.Ticket - [粉丝B]:已返回票房2020-06-26 21:27:12.715 [粉丝A] INFO com.hua.threadtest.state.Ticket - [粉丝A]:已购票2020-06-26 21:27 :12.715 [粉丝A] INFO com.hua.threadtest.state.Ticket - [粉丝A]:返回售票处 关于在 while 循环中放置 wait() 的问题 为什么要ticket.wait();放在 while (ticket.getNum() == 0) 代码块中?既然这行代码让线程等待,那么用if不就可以了吗? 我们想象一下,如果使用if,线程被唤醒后,会继续执行,而不判断条件是否满足。目前还没有票,粉丝也买不到票。 我们看一下官方文档对Object.wait()的描述:与单参数版本一样,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:synchronized (obj) { while () Obj.wait(); ... // 执行适合条件的操作 } 在参数版本(等待方法)中,中断和虚假唤醒是可能的,该方法应始终在循环中使用。 我们继续看Object.wait(long timeout)的文档: 线程还可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试应导致线程被唤醒的条件来防止这种情况,并在条件不满足时继续等待。换句话说,等待应该总是在循环中发生 线程也可以在没有通知、中断或超时的情况下被唤醒,这称为假唤醒。尽管这种情况在实践中很少发生,但应用程序必须通过测试导致线程被唤醒的条件来防止这种情况,并在条件不满足时继续等待。换句话说,等待应该总是在循环中发生 因此,为了避免在很少发生错误唤醒时程序出现不可预测的错误,建议将 wait() 调用放在循环语句中。这样一来,即使被误唤醒,仍然会受到条件语句的限制。 这也是为什么wait应该放在循环语句中的原因之一。BLOCKED和WAITING状态的区别和关系 表:等待状态下各种细分状态对比 简单来说,处于BLOCKED状态的线程仍然在竞争锁。一旦CPU有时间,竞争锁后就会执行。 但是,处于 WAITING 状态的线程不会竞争锁。他们需要等待被动通知或者自己设定的闹钟(等待时间)到来才去竞争锁。 一张图片胜过千言万语。这是一位外国专家画的图: 图:线程转换-详情.jpg 感谢您的阅读,希望对您有所帮助:) 来源:www.sychzs.cn/lienghua9112/article/details/106975105 结尾 ●【第301期】面试官:dubbo为什么不使用jdk的spi机制? ●【第302期】面试官:如何设计幂等接口? ●【第303期】如何理解算法的时间复杂度? ●【第304期】堆排序算法(详细流程图解) ●【第305期】面试官:你用过Redis吗?那么能否介绍一下Redis有哪些监控指标呢? ●【第306期】面试官:集群环境下定时任务多次执行的解决方案是什么? ●【第307期】采访者:蔚来是什么?蔚来的原理是什么? ●【第308期】采访者:kafka为什么这么高效? ●【第309期】阿里巴巴菜鸟网—面试经历记录 ●【第310期】面试官:你对MySQL主从、主从、读写分离了解多少? 而不是在网上搜索问题?还不赶快关注我们吧~ PS:因为公众号平台改变了推送规则,如果不想错过内容,记得看完后点击“阅读”并加个“星”,这样每次推送新文章,它将尽快出现在您的订阅中。在列表中。点击“关注”即可支持我们! 版权声明:本文内容由网友自愿贡献,本文所表达的观点仅代表作者自己的观点。本网站仅提供信息存储空间服务,不拥有任何所有权,也不承担相关法律责任。如果您发现本站有任何涉嫌侵权/非法内容,请发送邮件举报。一经核实,该网站将立即删除。 本文由斑马博客整理。本文链接为:https://www.sychzs.cn/index.php/post/8500.html

相关文章