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

【多线程】阻塞等待(Blocking Wait)(以Java为例)

【多线程】阻塞等待(Blocking Wait)(以Java为例)

本文来自于我关于多线程的系列文章。欢迎阅读、点评与交流
1.【多线程】互斥锁(Mutex)是什么?
2.【多线程】临界区(Critical Section)是什么?
3.【多线程】计算机领域中的各种锁
4.【多线程】信号量(Semaphore)是什么?
5.【多线程】信号量(Semaphore)常见的应用场景
6.【多线程】条件变量(Condition Variable)是什么?
7.【多线程】监视器(Monitor)是什么?
8.【多线程】什么是原子操作(Atomic Operation)?
9.【多线程】竞态条件(race condition)是什么?
10.【多线程】无锁数据结构(Lock-Free Data Structures)是什么?
11.【多线程】线程休眠(Thread Sleep)的底层实现
12.【多线程】多线程的底层实现
13.【多线程】读写锁(Read-Write Lock)是什么?
14.【多线程】死锁(deadlock)
15.【多线程】线程池(Thread Pool)
16.【多线程】忙等待/自旋(Busy Waiting/Spinning)
17.【多线程】阻塞等待(Blocking Wait)(以Java为例)
18.【多线程】阻塞等待(Blocking Wait)(以C++为例)
19.【多线程】屏障(Barrier)
20.【多线程硬件机制】总线锁(Bus Lock)是什么?
21.【多线程硬件机制】缓存锁(Cache Lock)是什么?

阻塞等待(Blocking Wait) 是一个并发编程中的核心概念之一。

1. 什么是阻塞等待?

阻塞等待指的是一个线程在执行过程中,由于某些条件暂时不满足(例如等待I/O操作完成、等待获取锁、等待另一个线程的结果等),而主动或被动地暂停自己的执行,让出CPU资源,进入一种“休眠”状态。直到它所等待的条件被满足后,才会被唤醒,重新进入就绪状态,等待CPU调度继续执行。

简单来说就是:线程停下来,等某个事情发生。

2. 为什么需要阻塞等待?

如果没有阻塞等待机制,线程在条件不满足时只能不停地循环检查(即“忙等待”或“自旋”),这会白白浪费宝贵的CPU时间片。

对比一下:

  • 阻塞等待:

    • 线程:”锁还没释放?那我先睡了,锁释放了记得叫醒我。“
    • 优点: 不占用CPU,节能高效。
    • 缺点: 线程切换会带来一定的上下文切换开销。
  • 忙等待:

    • 线程:”锁还没释放?我查一下…还没…我再查一下…还没…“
    • 优点: 响应及时,一旦条件满足可立即继续。
    • 缺点: 持续占用CPU,浪费资源,可能导致性能问题。

在绝大多数应用场景下,阻塞等待是更优的选择,因为CPU时间是宝贵的,应该留给真正需要计算的线程。

3. 常见的阻塞等待场景(附代码示例)

以下是一些在Java中典型的会导致线程阻塞等待的情况。

场景一:同步锁(synchronized 或 Lock)

当多个线程竞争同一个同步锁时,只有一个线程能成功获取,其他线程会被阻塞,直到锁被释放。

public class SynchronizedBlockingExample {private static final Object lock = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock) {System.out.println("线程1获取到锁,开始执行");try {Thread.sleep(3000); // 模拟耗时操作,持有锁} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1释放锁");}});Thread t2 = new Thread(() -> {System.out.println("线程2尝试获取锁...");synchronized (lock) { // 线程2会在这里阻塞等待,直到线程1释放锁System.out.println("线程2获取到锁,开始执行");}});t1.start();t2.start();}
}

输出:

线程1获取到锁,开始执行
线程2尝试获取锁...
(等待约3秒后)
线程1释放锁
线程2获取到锁,开始执行
场景二:等待通知机制(wait/notify)

线程可以在持有锁的情况下,主动调用 wait() 方法释放锁并进入等待状态,直到其他线程调用 notify()notifyAll() 将其唤醒。

public class WaitNotifyExample {private static final Object lock = new Object();private static boolean condition = false;public static void main(String[] args) throws InterruptedException {Thread waitingThread = new Thread(() -> {synchronized (lock) {System.out.println("等待线程:检查条件,条件不满足,开始等待");while (!condition) { // 必须用循环检查,防止虚假唤醒try {lock.wait(); // 释放锁,并进入WAITING状态(阻塞)} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("等待线程:条件满足,继续执行");}});Thread notifyingThread = new Thread(() -> {synchronized (lock) {System.out.println("通知线程:正在改变条件...");condition = true;lock.notifyAll(); // 唤醒所有在lock上等待的线程System.out.println("通知线程:已发出通知");}});waitingThread.start();Thread.sleep(1000); // 确保等待线程先启动并进入等待notifyingThread.start();}
}
场景三:线程合并(Thread.join)

一个线程等待另一个线程执行完毕。

public class JoinExample {public static void main(String[] args) throws InterruptedException {Thread workerThread = new Thread(() -> {try {System.out.println("工作线程开始工作...");Thread.sleep(2000);System.out.println("工作线程结束工作。");} catch (InterruptedException e) {e.printStackTrace();}});workerThread.start();System.out.println("主线程等待工作线程结束...");workerThread.join(); // 主线程在这里阻塞,等待workerThread执行完毕System.out.println("主线程继续执行。");}
}
场景四:阻塞队列(BlockingQueue)

当队列为空时,从队列中获取元素的操作会被阻塞;当队列已满时,向队列中添加元素的操作也会被阻塞。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class BlockingQueueExample {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); // 容量为3// 生产者线程Thread producer = new Thread(() -> {try {queue.put("Item1");queue.put("Item2");queue.put("Item3");System.out.println("生产者已放满3个物品,尝试放第4个...");queue.put("Item4"); // 这里会被阻塞,直到有消费者消费一个物品System.out.println("生产者成功放入第4个物品");} catch (InterruptedException e) {e.printStackTrace();}});// 消费者线程Thread consumer = new Thread(() -> {try {Thread.sleep(3000); // 模拟消费者启动慢String item = queue.take(); // 消费一个物品,唤醒被阻塞的生产者System.out.println("消费者取走了: " + item);} catch (InterruptedException e) {e.printStackTrace();}});producer.start();consumer.start();}
}

4. 线程的状态与阻塞

在Java的 Thread.State 枚举中,与阻塞等待相关的状态主要有:

  • BLOCKED: 线程等待获取一个同步锁(如进入synchronized块)而陷入的阻塞。这是被动阻塞。
  • WAITING: 线程主动调用 Object.wait(), Thread.join(), LockSupport.park() 等方法后进入的状态。需要其他线程将其唤醒。
  • TIMED_WAITING: 与 WAITING 类似,但设定了超时时间(如 Thread.sleep(millis), Object.wait(timeout), Thread.join(timeout))。

总结

特性阻塞等待忙等待
CPU占用不占用持续占用
实现机制依靠操作系统/虚拟机线程调度循环检查条件
响应速度较慢(需要上下文切换)很快(条件满足立即执行)
适用场景绝大多数高并发、I/O密集型场景低竞争、等待时间极短的场景(通常用自旋锁

理解阻塞等待是编写高效、正确并发程序的基础。它帮助你管理线程间的协作,避免资源竞争,并有效利用系统资源。

http://www.dtcms.com/a/474559.html

相关文章:

  • 公众号做 视频网站商品行情软件下载
  • Kubernetes环境下Nginx代理Nacos服务请求故障诊断
  • Linux 文件权限详解与实操命令
  • 1Docker镜像与容器,目录挂载和卷映射的选择
  • 06_k8s数据持久化
  • c 教学网站开发网页设计尺寸大小规范
  • 第一章:AI大模型基本原理及API应用——第一小节
  • 购物便宜的网站有哪些vivo即将发布的新手机
  • 超级玛丽demo9
  • 汕头站扩建什么时候完成做单屏网站 高度是多少
  • 【Swift】LeetCode 1. 两数之和
  • CI/CD流水线实战:从零搭建到高效部署
  • AprioriFP-Growth算法详解
  • 吕梁网站定制wordpress登录注册页面模板
  • 网站列表页是啥求个网站这么难吗2021年
  • wordpress如何制作网站做影片的网站描述
  • Java Redis “高可用 — 主从复制”面试清单(含超通俗生活案例与深度理解)
  • etcd实战课-实战篇(下)
  • 定制一个网站多少钱企业做网站有用吗天涯
  • 05-k8s网络
  • Stable Diffusion 安装教程(详细)_stable diffusion安装
  • 做网站的dw全称是啥免费软件视频
  • 开源TTS项目 Neutts-Air:架构、训练、推理与应用全景
  • python--手势识别
  • 烟台网站建设设计国内哪家网站建设公司好
  • 实操三、使用cgroups对cpu进行控制
  • 广东建设工程造价管理协会网站网站分析数据
  • Python基础入门例程100-NP100 重载运算(涉及类-难)
  • 路漫漫-数据结构与算法邂逅Java
  • 上海学做网站筑龙网app下载