ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java线程生命周期全解析:从NEW到TERMINATED!

Java线程生命周期全解析:从NEW到TERMINATED! 全文目录开篇语一、线程生命周期与状态转换1. NEW刚创建还没“开工”2. RUNNABLE正在 CPU 上排队 / 跑着3. BLOCKED等着进“临界区”的锁4. WAITING无限期等待某个条件5. TIMED_WAITING带闹钟的等待6. TERMINATED线程结束了7. 状态转换小总结文字版“状态图”二、Thread vs Runnable vs Callable谁负责干活谁只是“任务说明书”1. Thread线程本体 可选“任务”2. Runnable没有返回值的“任务接口”3. Callable有返回值的任务接口配合 Future三、sleep() vs wait()都“睡觉”但一个只是打盹一个是“放弃座位排队重来”1. 来个对比大表先压压惊2. sleep()谁也不管我就静静躺一会儿3. wait()把锁先让出来等别人叫我再继续4. 一个经典生产者-消费者对比示例使用 wait() / notify() 实现简单阻塞队列5. sleep() vs wait() 再来几句“人话”版总结四、最后帮你串一下三块知识文末开篇语哈喽各位小伙伴们你们好呀我是喵手。运营社区C站/掘金/腾讯云/阿里云/华为云/51CTO欢迎大家常来逛逛今天我要给大家分享一些自己日常学习到的一些知识点并以文字的形式跟大家一起交流互相学习一个人虽可以走的更快但一群人可以走的更远。我是一名后端开发爱好者工作日常接触到最多的就是Java语言啦所以我都尽量抽业余时间把自己所学到所会的通过文章的形式进行输出希望以这种方式帮助到更多的初学者或者想入门的小伙伴们同时也能对自己的技术进行沉淀加以复盘查缺补漏。小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持一、线程生命周期与状态转换Java 里线程的状态是Thread.State这个枚举定义的常见的就六个NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED你可以把一个线程想象成人在公司上班的一天刚入职、排队打卡、占用会议室、等别人开会、定时闹钟、最后下班走人。1. NEW刚创建还没“开工”ThreadtnewThread(()-{System.out.println(running...);});System.out.println(t.getState());// NEW只调用了new Thread(...)还没start()仅仅是个“Java 对象”还没有真正对应到底层系统线程2. RUNNABLE正在 CPU 上排队 / 跑着start()之后线程进入RUNNABLEt.start();System.out.println(t.getState());// 通常是 RUNNABLE注意在 Java 里RUNNABLE包含了“就绪 运行中”两种状态具体是正在 CPU 上跑还是在就绪队列里等JVM 不对你细分典型进入RUNNABLE的路径从NEW调用start()从BLOCKED拿到锁从WAITING/TIMED_WAITING被唤醒且拿到锁从 I/O 阻塞恢复某些实现会表现为 RUNNABLE3. BLOCKED等着进“临界区”的锁BLOCKED专门表示线程在等待一个 synchronized 的监视器锁intrinsic lock。例如finalObjectlocknewObject();Threadt1newThread(()-{synchronized(lock){try{Thread.sleep(100000);}catch(InterruptedExceptione){}}});Threadt2newThread(()-{synchronized(lock){System.out.println(t2 got lock);}});t1.start();Thread.sleep(100);// 让 t1 先拿到锁t2.start();Thread.sleep(100);System.out.println(t2.getState());// 可能是 BLOCKED这里t1先进入 synchronized 块并sleep一直占着锁t2想进 synchronized 块发现锁被占用 →BLOCKED当t1出 synchronizedt2才能从BLOCKED→RUNNABLE4. WAITING无限期等待某个条件WAITING表示线程在无限期等待直到被别的线程显式唤醒。常见导致WAITING的方法Object.wait()无超时Thread.join()无超时LockSupport.park()例子wait / notifyfinalObjectlocknewObject();ThreadtnewThread(()-{synchronized(lock){try{lock.wait();// 线程进入 WAITING并且释放锁System.out.println(被唤醒啦);}catch(InterruptedExceptione){}}});t.start();Thread.sleep(100);System.out.println(t.getState());// WAITINGsynchronized(lock){lock.notify();}特点线程会释放锁和BLOCKED不同必须等到别人notify()/notifyAll()/ interrupt 才可能继续5. TIMED_WAITING带闹钟的等待TIMED_WAITING表示线程在有“时间上限”的等待时间到了会自动醒。典型来源Thread.sleep(millis)Object.wait(timeout)带超时的Thread.join(timeout)LockSupport.parkNanos()/parkUntil()例子ThreadtnewThread(()-{try{Thread.sleep(10000);// 10 秒}catch(InterruptedExceptione){}});t.start();Thread.sleep(100);System.out.println(t.getState());// TIMED_WAITING超时到了线程从TIMED_WAITING→RUNNABLE如果是wait(timeout)还需要重新拿锁才能继续执行6. TERMINATED线程结束了当run()或call()执行完线程就进入TERMINATEDThreadtnewThread(()-{System.out.println(done);});t.start();t.join();// 等它结束System.out.println(t.getState());// TERMINATED7. 状态转换小总结文字版“状态图”可以把线程的一生理解为NEW→start→RUNNABLERUNNABLE→进入synchronized前抢锁失败 →BLOCKED调用wait()/join()/park()→WAITING调用sleep()/wait(timeout)/join(timeout)/parkNanos→TIMED_WAITINGBLOCKED/WAITING/TIMED_WAITING→拿到锁、被唤醒、超时、被中断 等 → 回到RUNNABLE最终run 执行完 →TERMINATED二、Thread vs Runnable vs Callable谁负责干活谁只是“任务说明书”你可以这么理解Runnable/Callable任务说明书Thread真正的“工人”线程池ExecutorService工头 员工队列1. Thread线程本体 可选“任务”Thread是一个类代表一个线程对象它内部有一个run()方法可以直接继承Thread重写run()也可以把Runnable丢进去让它帮你跑示例 1继承 Thread不太推荐classMyThreadextendsThread{Overridepublicvoidrun(){System.out.println(I am a Thread);}}newMyThread().start();问题类已经被占用不能再继承其它父类可扩展性较差。2. Runnable没有返回值的“任务接口”Runnable是一个函数式接口只定义了一个void run()Runnabletask()-{System.out.println(running in Thread.currentThread().getName());};newThread(task).start();特点没有返回值不能抛 checked exception只能自己 try-catch适合那些只需要“干完就完了”的任务在使用线程池时execute(Runnable command)就是跑这种任务。3. Callable有返回值的任务接口配合 FutureCallableV是比Runnable更“高级”一点的任务描述方法是V call() throws Exception;有返回值可以抛 checked exception典型用法配合ExecutorService.submit()和Future使用importjava.util.concurrent.*;CallableIntegertask()-{Thread.sleep(1000);return42;};ExecutorServicepoolExecutors.newFixedThreadPool(2);FutureIntegerfuturepool.submit(task);System.out.println(doing something else...);Integerresultfuture.get();// 阻塞等待结果System.out.println(result result);pool.shutdown();对比一下对象类型核心方法返回值是否能抛 checked 异常常见使用场景Thread类run()/start()void不支持run 不能抛直接表示一个线程Runnable函数式接口run()void不支持提交到线程池、给 Thread 跑Callable函数式接口call()任意类型V支持有返回值、有异常的异步任务一句直白总结想要返回值用CallableFuture。不需要返回值只是跑一下逻辑Runnable就够了。不要随便 new Thread优先线程池。三、sleep() vs wait()都“睡觉”但一个只是打盹一个是“放弃座位排队重来”这俩名字都很迷惑新人但本质区别非常关键。1. 来个对比大表先压压惊对比点Thread.sleep()Object.wait()所在类ThreadObject是否释放锁❌ 不释放锁✅ 释放对象监视器锁synchronized 的那个锁是否必须在 synchronized 里调用❌ 不要求✅ 必须持有该对象的 monitor否则抛IllegalMonitorStateException唤醒方式时间到了 / 被中断notify()/notifyAll()/ 时间到 / 被中断典型用途单纯让线程暂停一段时间不用于通信线程间协作 / 条件等待生产者-消费者等所属状态通常进入TIMED_WAITINGWAITING或TIMED_WAITING2. sleep()谁也不管我就静静躺一会儿try{Thread.sleep(1000);// 当前线程睡 1 秒}catch(InterruptedExceptione){// 响应中断}特点当前线程进入TIMED_WAITING不会释放已经占有的任何锁比如你在 synchronized 里 sleep时间到后自动恢复到RUNNABLE中途被interrupt()会抛InterruptedException并清除中断标志比如synchronized(lock){Thread.sleep(10000);// 期间别人拿不到这个 lock}这就是为什么在synchronized里长时间sleep()是个风险操作你拿着锁睡觉别人全被堵在外面。3. wait()把锁先让出来等别人叫我再继续wait()是配合 synchronized 使用的线程协作机制。finalObjectlocknewObject();Threadt1newThread(()-{synchronized(lock){try{System.out.println(t1: wait before);lock.wait();// 1. 释放 lockSystem.out.println(t1: resumed);}catch(InterruptedExceptione){}}});Threadt2newThread(()-{synchronized(lock){System.out.println(t2: notify);lock.notify();}});关键行为调用lock.wait()前当前线程必须已经持有lock的锁在 synchronized 块/方法中调用wait()后当前线程进入WAITING或TIMED_WAITING释放该对象的锁让别的线程可以拿到这个锁干活其他线程在同一个对象锁上调用notify()/notifyAll()后等待中的线程会被唤醒重新去抢这个锁抢到锁之后从wait()之后继续执行4. 一个经典生产者-消费者对比示例使用wait()/notify()实现简单阻塞队列classMyBlockingQueue{privatefinalQueueIntegerqueuenewLinkedList();privatefinalintcapacity;publicMyBlockingQueue(intcapacity){this.capacitycapacity;}publicvoidput(intvalue)throwsInterruptedException{synchronized(queue){while(queue.size()capacity){queue.wait();// 队列满了释放锁等待消费者消费}queue.add(value);queue.notifyAll();// 通知可能在等的消费者}}publicinttake()throwsInterruptedException{synchronized(queue){while(queue.isEmpty()){queue.wait();// 队列空了释放锁等生产者放数据}intvalqueue.poll();queue.notifyAll();// 通知可能在等的生产者returnval;}}}这里用的是典型的条件等待 释放锁的模式sleep()完全干不了这事。如果你用sleep()来轮询队列是否为空不仅浪费 CPU频繁检查还不精确时间控制难。5. sleep() vs wait() 再来几句“人话”版总结sleep()“我就在这儿等一会儿谁也别动我的东西锁时间到了我自己继续跑。”wait()“我先把座位锁让出来大家先忙等你们叫我notify我再抢座回来继续干。”所以想要简单延时sleep()想要线程之间协作 / 条件同步wait()/notify()或更现代的 Lock / Condition / BlockingQueue 等四、最后帮你串一下三块知识如果把刚才三块揉成一个脑图线程状态启动NEW → RUNNABLE等锁BLOCKEDsynchronized等条件 / 等别人叫WAITING / TIMED_WAITINGwait/join/park/sleep结束TERMINATED任务 vs 线程Runnable/Callable 任务描述Thread 真正线程线程池 管线程 执行任务sleep vs wait都会让线程进入TIMED_WAITING最大差别是否释放锁是否需要别人“叫醒”sleep不管别人就自己暂停wait用于线程协调释放锁配合notify/notifyAll… …文末好啦以上就是我这期的全部内容如果有任何疑问欢迎下方留言哦咱们下期见。… …学习不分先后知识不分多少事无巨细当以虚心求教三人行必有我师焉wished for you successed ⭐️若喜欢我就请关注我叭。⭐️若对您有用就请点赞叭。⭐️若有疑问就请评论留言告诉我叭。版权声明本文由作者原创转载请注明出处谢谢支持
返回列表