当前位置: 首页 > news >正文

【JUC】线程

一、线程的状态及流转

Java 中线程状态定义在Thread.State枚举类中,一共有6 种状态

public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }

可以这样理解:


1. NEW:新建状态

当你只是创建了线程对象,但还没有调用start()方法时,线程处于NEW 状态

Thread t = new Thread();

此时只是 JVM 中有了一个线程对象,真正的操作系统线程还没有启动。


2. RUNNABLE:可运行状态

当调用了start()方法之后,线程进入RUNNABLE状态。

t.start();

注意,Java 中没有单独区分“就绪”和“运行”。

在操作系统层面,线程可能有两种情况:

就绪:等待 CPU 调度 运行:正在 CPU 上执行

但是在 Java 的Thread.State中,这两种情况都统一叫:

RUNNABLE

所以可以理解为:

调用 start() NEW --------------> RUNNABLE 抢到 CPU 执行权 RUNNABLE ---------> 正在运行 CPU 时间片用完 正在运行 ---------> RUNNABLE

但是 Java 代码里查看状态时,看到的还是RUNNABLE


3. BLOCKED:阻塞状态

当线程想进入synchronized修饰的代码块或方法,但是锁被其他线程持有时,就会进入BLOCKED状态。

例如:

synchronized (lock) { // 临界区代码 }

假设 A 线程已经拿到了lock锁,B 线程也想进入这段代码,那么 B 线程就会进入:

BLOCKED

等 A 线程释放锁之后,B 线程才有机会重新进入RUNNABLE状态。

重点:

BLOCKED 是等待 synchronized 锁。

4. WAITING:无限等待状态

线程调用某些方法后,会进入无限等待状态,直到被其他线程唤醒。

常见方法有:

Object.wait(); Thread.join(); LockSupport.park();

比如:

lock.wait();

线程会释放锁,并进入WAITING状态,直到其他线程调用:

lock.notify(); lock.notifyAll();

再比如:

bThread.join();

A 线程调用bThread.join()后,会等待 B 线程执行完毕。此时 A 线程也会进入等待状态。

重点:

WAITING 是无限等待,必须等别人唤醒或等待的线程结束。

5. TIMED_WAITING:计时等待状态

线程调用带有时间参数的方法时,会进入TIMED_WAITING状态。

常见方法:

Thread.sleep(1000); Object.wait(1000); Thread.join(1000); LockSupport.parkNanos(); LockSupport.parkUntil();

比如:

Thread.sleep(2000);

线程会睡眠 2 秒,2 秒后自动回到RUNNABLE状态。

重点:

TIMED_WAITING 是有限时间等待,时间到了可以自动恢复。

6. TERMINATED:终止状态

当线程的run()方法执行完毕,或者执行过程中抛出未处理异常,线程就会进入终止状态。

public void run() { System.out.println("执行任务"); }

执行完之后:

RUNNABLE -> TERMINATED

线程结束后,不能再次调用start()

如果再次调用:

t.start(); t.start();

会报错:

IllegalThreadStateException

二、线程状态流转总结

可以这样记:

创建线程对象 NEW 调用 start() NEW -> RUNNABLE 抢到 CPU RUNNABLE -> 运行中 但是 Java 中仍然显示为 RUNNABLE 等待 synchronized 锁 RUNNABLE -> BLOCKED 调用 wait/join/park RUNNABLE -> WAITING 调用 sleep/wait(time)/join(time) RUNNABLE -> TIMED_WAITING run 方法执行结束 RUNNABLE -> TERMINATED

面试中可以这样回答:

Java 线程在 Thread.State 中定义了六种状态,分别是 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。线程创建后是 NEW,调用 start 后进入 RUNNABLE。Java 中的 RUNNABLE 包含操作系统层面的就绪和运行。线程竞争 synchronized 锁失败会进入 BLOCKED,调用 wait、join 等方法会进入 WAITING,调用 sleep 或带时间参数的 wait、join 会进入 TIMED_WAITING,run 方法执行结束后进入 TERMINATED。

三、创建线程的方式

方式一:继承 Thread 类

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("线程执行");}}publicclassDemo{publicstaticvoidmain(String[]args){newMyThread().start();}}

特点:

简单直接,但是 Java 是单继承,继承 Thread 后就不能继承其他类。

方式二:实现 Runnable 接口

classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("线程执行");}}publicclassDemo{publicstaticvoidmain(String[]args){Threadt=newThread(newMyRunnable());t.start();}}

特点:

更推荐,任务和线程分离,避免单继承限制。

也可以用 Lambda:

newThread(()->{System.out.println("线程执行");}).start();

方式三:实现 Callable 接口结合 FutureTask

Runnable没有返回值,也不能直接抛出受检异常。

如果任务需要返回结果,可以使用Callable

importjava.util.concurrent.Callable;importjava.util.concurrent.FutureTask;publicclassDemo{publicstaticvoidmain(String[]args)throwsException{Callable<Integer>callable=()->{return100;};FutureTask<Integer>futureTask=newFutureTask<>(callable);Threadt=newThread(futureTask);t.start();Integerresult=futureTask.get();System.out.println(result);}}

特点:

适合需要返回值的异步任务。 futureTask.get() 会阻塞当前线程,直到任务执行完并返回结果。

方式四:通过线程池创建线程

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;publicclassDemo{publicstaticvoidmain(String[]args){ExecutorServicepool=Executors.newFixedThreadPool(3);pool.execute(()->{System.out.println("线程池执行任务");});pool.shutdown();}}

特点:

实际开发中更推荐使用线程池。

因为线程池可以:

复用线程 减少频繁创建和销毁线程的开销 统一管理线程资源 提高系统稳定性

四、多线程的应用场景

多线程适合处理一些耗时任务,避免主线程被长时间阻塞。

比如:

文件拷贝 文件上传下载 加载大量资源 网络请求 聊天软件收发消息 后台服务器处理多个请求 定时任务 日志异步写入 消息队列消费

举个例子:

如果一个服务器只有一个线程,那么同时来了 100 个请求,只能一个一个处理。

使用多线程后,可以多个请求同时被处理,提高并发能力。


五、A 线程启动 B 线程,A 要等 B 执行完再继续,怎么实现?

这个问题的核心是:

线程之间的协作 / 等待另一个线程执行完成

常见方法有三种:

Thread.join()CountDownLatchCompletableFuture

方法一:Thread.join()

代码

publicclassJoinDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadbThread=newThread(()->{System.out.println("B 线程开始执行");try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("B 线程执行完毕");});bThread.start();bThread.join();System.out.println("A 线程继续执行");}}

原理

A 线程调用:

bThread.join();

意思是:

A 等待 bThread 执行结束。

所以执行流程是:

A 线程启动 A 创建 B 线程 A 调用 bThread.start() B 开始执行 A 调用 bThread.join(),A 阻塞等待 B 执行完毕 A 从 join() 返回 A 继续执行

执行顺序:

B 线程开始执行 B 线程执行完毕 A 线程继续执行

注意:

bThread.join();

阻塞的是调用 join 的线程,也就是 A 线程,不是 B 线程。


方法二:CountDownLatch

CountDownLatch可以理解为一个倒计时计数器。

代码

importjava.util.concurrent.CountDownLatch;publicclassCountDownLatchDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{CountDownLatchlatch=newCountDownLatch(1);ThreadbThread=newThread(()->{System.out.println("B 线程开始执行");try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("B 线程执行完毕");latch.countDown();});bThread.start();latch.await();System.out.println("A 线程继续执行");}}

原理

CountDownLatchlatch=newCountDownLatch(1);

表示需要等待 1 个任务完成。

A 线程调用:

latch.await();

意思是:

只要计数器不为 0,A 就一直等。

B 线程执行完之后调用:

latch.countDown();

意思是:

计数器减 1。

计数器从 1 变成 0 后,A 线程就会被唤醒,然后继续执行。

流程:

A 创建 CountDownLatch,计数器为 1 A 启动 B 线程 A 调用 await(),等待计数器变成 0 B 执行任务 B 执行完调用 countDown() 计数器变成 0 A 被唤醒 A 继续执行

CountDownLatch 适合什么场景?

join()适合等待某一个具体线程。

CountDownLatch更适合等待多个任务完成。

例如 A 线程要等 B、C、D 三个线程都执行完:

CountDownLatchlatch=newCountDownLatch(3);

每个线程执行完:

latch.countDown();

A 线程:

latch.await();

只有计数器变成 0,A 才继续。


方法三:CompletableFuture

CompletableFuture是 Java 8 引入的异步编程工具。

代码

importjava.util.concurrent.CompletableFuture;publicclassCompletableFutureDemo{publicstaticvoidmain(String[]args){System.out.println("A 线程启动 B 线程");CompletableFuture.runAsync(()->{System.out.println("B 线程执行");try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("B 线程执行完毕");}).join();System.out.println("A 线程继续执行");}}

原理

CompletableFuture.runAsync(()->{// B 线程执行的任务})

这行代码会把任务提交到默认线程池中异步执行。

默认使用的是:

ForkJoinPool.commonPool()

也就是说,B 任务不是由 A 线程执行,而是由线程池里的其他线程执行。

然后:

.join();

表示:

A 线程等待这个异步任务执行完成。

所以流程是:

A 线程启动 A 提交异步任务 线程池中的某个线程执行 B 任务 A 调用 join() 等待异步任务完成 B 任务执行完毕 A 继续执行

CompletableFuture 中的 join 和 get 区别

CompletableFuture也可以用:

future.get();

例如:

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { System.out.println("B 线程执行"); }); future.get();

区别是:

get() 需要处理受检异常:InterruptedException、ExecutionException join() 不需要显式处理受检异常,会把异常包装成 CompletionException

所以:

future.join();

写起来更简单。

六、三种方式怎么选?

可以这样总结:

方法适合场景特点
Thread.join()A 等待一个具体线程 B 执行完简单直接
CountDownLatchA 等待一个或多个线程/任务完成适合多线程协作
CompletableFuture异步任务编排适合链式异步任务、组合任务、有返回值任务

面试回答可以这样说

如果 A 线程启动 B 线程后,需要等待 B 线程执行完再继续,可以使用 Thread.join()。A 线程调用 bThread.join() 后会阻塞,直到 B 线程执行结束。也可以使用 CountDownLatch,把计数器初始化为 1,A 调用 await() 等待,B 执行完后调用 countDown(),计数器归零后 A 继续执行。如果是异步编程场景,也可以使用 CompletableFuture,比如 CompletableFuture.runAsync() 启动异步任务,然后调用 join() 等待任务完成。

重点区别

你可以重点记住这句话:

join 是等一个具体线程结束; CountDownLatch 是等一个或多个任务完成; CompletableFuture 是异步任务编排工具,可以等待任务完成,也可以处理返回值和后续任务。

另外注意:

sleep 不会释放锁; wait 会释放锁; join 本质上是当前线程等待目标线程结束; CountDownLatch 的 await 会阻塞当前线程; CompletableFuture 的 join 会阻塞当前线程等待异步任务完成。

七、一句话区别wait和await

wait 是 Object 的方法,配合 synchronized 使用; await 是并发工具类的方法,常见于 CountDownLatch、Condition 等。

1. wait 是什么?

wait()Object类的方法。

也就是说,任何对象都可以调用:

lock.wait();

但它必须在synchronized代码块或同步方法中使用。

例如:

synchronized (lock) { lock.wait(); }

否则会抛异常:

IllegalMonitorStateException

2. wait 的特点

调用wait()后:

当前线程会释放锁 当前线程进入 WAITING 状态 需要其他线程调用 notify() 或 notifyAll() 唤醒

例子:

Object lock = new Object(); Thread t1 = new Thread(() -> { synchronized (lock) { try { System.out.println("t1 开始等待"); lock.wait(); System.out.println("t1 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { synchronized (lock) { System.out.println("t2 唤醒 t1"); lock.notify(); } });

t1调用wait()后会释放lock,然后等待t2调用notify()

3. await 是什么?

await()不是Object的方法,它通常出现在 JUC 并发工具类中。

常见的有两类:

CountDownLatch.await(); Condition.await(); CyclicBarrier.await();

它们虽然都叫await,但含义不完全一样。


4. CountDownLatch.await()

你前面代码里的await()是这个:

latch.await();

它的作用是:

当前线程等待,直到 CountDownLatch 的计数器变成 0。

例如:

CountDownLatch latch = new CountDownLatch(1); Thread b = new Thread(() -> { System.out.println("B 线程执行任务"); latch.countDown(); }); b.start(); latch.await(); System.out.println("A 线程继续执行");

流程是:

A 调用 latch.await() A 阻塞等待 B 执行完任务 B 调用 latch.countDown() 计数器从 1 变成 0 A 被唤醒,继续执行

这里的重点是:

CountDownLatch.await() 等的是计数器归零。

它不需要配合synchronized使用。


5. Condition.await()

还有一种await()Condition的方法。

它和wait()比较像。

Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); lock.lock(); try { condition.await(); } finally { lock.unlock(); }

它必须在获取Lock之后调用。

对应关系可以这样看:

synchronized 体系Lock 体系
synchronizedReentrantLock
wait()Condition.await()
notify()Condition.signal()
notifyAll()Condition.signalAll()

所以:

lock.wait();

对应的是:

condition.await();

而不是CountDownLatch.await()


6. 核心区别

对比点wait()CountDownLatch.await()Condition.await()
所属类ObjectCountDownLatchCondition
使用前提必须在synchronized不需要synchronized必须先获取Lock
是否释放锁会释放synchronized不涉及锁释放会释放Lock
唤醒方式notify()/notifyAll()countDown()使计数器归零signal()/signalAll()
主要用途线程通信等待多个任务完成Lock 体系下的线程通信
http://www.jsqmd.com/news/862755/

相关文章:

  • 紧急更新!Midjourney刚悄悄关闭阿盖洛印相的raw模式入口:最后48小时掌握未阉割版--agallo-legacy参数调用秘径
  • 2026最新诚信优选 重庆市铜梁区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026:AI超级员工崛起,谁是真正的市场赢家?
  • 2026最新诚信优选 重庆市开州区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 设计模式之建造者
  • Transformer详解
  • 2026最新诚信优选 重庆市梁平区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 深圳电商财税公司推荐top8,商家选型参考!
  • 2026最新诚信优选 重庆市潼南区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026年信创考勤系统推荐与选型对比:政策要求及5款主流产品全解析
  • C++内联函数性能分析
  • 2026最新诚信优选 上海市宝山区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 重庆市南岸区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 【JUC】线程池
  • [Unity实战] Shader 学了很多却提不动项目性能?问题往往出在没把渲染知识接回场景优化
  • 2026最新诚信优选 重庆市万州区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026年京东云OpenClaw/Hermes Agent配置Token Plan搭建保姆教程
  • 2026最新诚信优选 重庆市武隆区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 临汾市尧都区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • okbiye 双降神器:把论文重复率 + AIGC 率 “一键清零”,毕业季再也不用慌
  • C++内存对齐与布局优化
  • 2026最新诚信优选 重庆市南川区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 上海市崇明区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 重庆市永川区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 【AI入门知识点】Skills 是什么?终于有人把 Skills、Function Calling、MCP 讲明白了
  • 一键营造立体感!OBS“半透明滤镜”上线,让直播间层次分明
  • 2026最新诚信优选 重庆市綦江区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 吕梁市离石区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 论文降重 + 消 AIGC 双难题?用 okbiye,我帮室友一周搞定毕业查重危机
  • TCP拥塞控制详解