阻塞和非阻塞

在介绍阻塞和非阻塞之前,我们先来了解一下多线程间一个重要的概念——临界区。

临界区——一种公有的资源或者共享数据,它可以被多个线程使用。临界区资源一次只能被一个线程使用,其它线程必须等待上一个线程
执行完成之后,才能使用。临界区资源是多线程之间重要保护对象,当临界区资源同时被多个线程访问时,容易出现错误。
代码示例:
/**
 * @author php
 * @date 2018/7/2
 */
public class CriticalRegion {
    //定义1000元
    public static Integer money = 1000;

    public static void main(String[] args) throws InterruptedException {
        //定义公共资源
        CriticalRegion criticalRegion = new CriticalRegion();
        //定义10个线程来购物
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                criticalRegion.shop();
            });
        }
        Thread.sleep(1000);
        System.out.println("最终还有:" + CriticalRegion.money);
        executorService.shutdown();
    }

    //我们每次购物使用100块钱
    public  void shop() {
        money = money - 100;
        System.out.println("使用了100元,我还有:" + CriticalRegion.money);
    }
}

执行结果:从中可以看出,多个线程,同时操作临界区资源时,容易导致操作资源错误。在大部分情况下,这是一个很严重的问题。
大家可以多执行几次,看看结果有什么不同。


现在,我们修改一下shop()方法,在方法上加上sychronized。执行结果:sychronized可以让临界区资源,一次只能被一个线程访问,

从而结果正确。


临界区资源,在使用多线程时,要格外的注意,以免结果千奇百怪。

在我们了解临界区之后,再来了解阻塞和非阻塞的概念。

阻塞和非阻塞通常被用来形容多线程间的相互影响。当一个线程占用了临界区资源,那么其它需要使用这个资源的线程都必须在这个临界区上等待。等待会导致线程挂起,这样就形成了阻塞。如果占用资源的线程一直没有释放资源,那么其它的线程在这个临界区上都不能继续工作。

相反,非阻塞表明多个线程之间的执行是不会相互影响的。

通常,我们使用synchronized关键字,ReentrantLock(重入锁)时,我们得到的线程就是阻塞线程。阻塞线程在执行代码前,都会尝试得到临界区资源的锁,如果得不到,线程就会一直挂起,直到临界区资源释放。
<think>我们正在讨论阻塞非阻塞的概念。用户要求了解它们的定义,并参考了之前的引用内容。根据引用[1][2][3][5],我们可以总结如下: 阻塞(Blocking)是一个操作(如I/O、通信等)在执行时,如果条件不满足(如数据未准备好、资源不可用等),调用者会一直等待,直到操作完成才返回。在此期间,调用者(如线程进程)会被挂起,不能执行其他任务。 非阻塞(Non-Blocking)则是操作在执行时,如果条件不满足,会立即返回一个错误码(特定状态),而不会等待。调用者可以继续执行其他任务,并通过轮询回调等方式来检查操作是否完成。 结合引用[4]中提到的非阻塞通信,非阻塞操作允许初始化一个操作后立即返回,然后通过后续的完成调用来检查操作是否完成,这样可以在操作进行的同时执行其他计算(重叠通信计算)。 因此,我们可以这样定义: **阻塞**:调用方发起一个操作后,必须等待该操作完全完成(包括等待资源就绪、数据传输等)才能继续执行后续代码。期间调用方处于等待状态(可能被系统挂起)。 **非阻塞**:调用方发起一个操作后,无论操作是否完成都立即返回。调用方可以继续执行后续代码,并通过其他方式(如循环查询、回调函数、事件通知等)来获取操作结果。 接下来,我们将按照用户的要求,使用数学表达式格式(如果需要的话),并生成相关问题。由于定义本身不涉及复杂数学,我们主要用文字描述。 此外,我们会在适当位置添加引用标识。 </think>### 阻塞非阻塞的定义 在系统编程中,**阻塞**与**非阻塞**是两种关键的操作模式,其核心区别在于**调用方是否等待操作完成**: #### 1. **阻塞(Blocking)** - **定义**: 当调用方发起操作时,必须**等待该操作完全完成**才能继续执行后续代码。在此期间,调用方线程/进程被挂起,不消耗CPU资源。 $$ \text{调用操作} \rightarrow \text{等待完成} \rightarrow \text{返回结果} \rightarrow \text{继续执行} $$ - **典型场景**: - 读取未就绪的I/O设备时线程休眠(如磁盘、网络)[^5] - 消息队列满时,`msgsnd()` 默认阻塞至空间可用[^1] - `future.get()` 同步等待异步任务结果[^3] #### 2. **非阻塞(Non-Blocking)** - **定义**: 调用方发起操作后**立即返回**,无论操作是否完成。调用方需通过轮询/回调获取结果,期间可执行其他任务。 $$ \text{调用操作} \rightarrow \text{立即返回} \rightarrow \begin{cases} \text{轮询结果} \\ \text{执行其他任务} \end{cases} $$ - **典型实现**: - 设置标志位(如 `IPC_NOWAIT`)使 `msgsnd()` 立即返回错误码[^1] - 非阻塞I/O读取设备时直接返回 `EAGAIN` 错误[^5] - 拆分通信操作:初始化(`send_init`)与完成检查(`send_complete`)分离[^4] --- ### 核心差异对比 | **特性** | **阻塞模式** | **非阻塞模式** | |------------------|--------------------------------------|-----------------------------------| | **调用返回时机** | 操作完成后返回 | 立即返回 | | **资源占用** | 等待期间不占用CPU | 需主动轮询消耗CPU | | **编程复杂度** | 逻辑简单(顺序执行) | 需处理状态机/回调(复杂度高) | | **适用场景** | 实时性要求低、简单逻辑 | 高并发、需重叠计算与通信的系统[^4] | > **关键洞察**:异步非阻塞性能不一定优于同步阻塞,需结合硬件支持与任务特性[^2]。例如: > - 重叠计算与通信时,非阻塞可提升吞吐量$$ \text{吞吐量} \propto \frac{\text{计算时间} + \text{通信时间}}{\text{计算时间} \times \text{通信时间}} $$ > - 但频繁轮询会导致CPU空转,降低能效。 ---
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值