【并发设计模式】聊聊等待唤醒机制的规范实现

在多线程编程中,其实就是分工、协作、互斥。在很多场景中,比如A执行的过程中需要同步等待另外一个线程处理的结果,这种方式下,就是一种等待唤醒的机制。本篇我们来讲述等待唤醒机制的三种实现,以及对应的应用场景。

Guarded Suspension 模式

Guarded Suspension 翻译过来就是保护性暂停。其实就是一个线程需要等待获取另外一个线程执行的结果,先把当前线程挂起,另外一个线程执行完毕之后,通知自己,结束阻塞状态,继续执行。

等待唤醒的规范实现如下:

  • sychronized+wait/notify/notifyAll
  • reentrantLock+Condition(await/singal/singalAll)
  • cas+park/unpark

其实底层以来的是pthread,pthread_mutex_lock/unlock pthread_cond_wait/singal。这里就不介绍了,感兴趣的朋友可以自行查阅。

解决线程之间的协作不可避免会用到阻塞唤醒机制

实际编码

syn

java 复制代码
package com.jia.suspension;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author qxlx
 * @date 2023/12/30 3:13 PM
 */
public class SynTest {

    private Object obj;

    public Object read() {
        synchronized (this) {
            while (Objects.isNull(obj)) {
                try {
                    System.out.println(Thread.currentThread().getName()+  " wait-before");
                    this.wait();
                    System.out.println(Thread.currentThread().getName()+  " wait-after");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return obj;
        }
    }

    public void write() {
        System.out.println(Thread.currentThread().getName()+ "  write");
        synchronized (this) {
            obj = new Object();
            System.out.println(Thread.currentThread().getName()+ "  notifyAll-before");
            this.notifyAll();
            System.out.println(Thread.currentThread().getName()+ "   notifyAll-after");
        }
    }


    public static void main(String[] args) throws InterruptedException {
        SynTest synTest = new SynTest();

        new Thread(()-> {
            synTest.read();
        }).start();

        new Thread(()-> {
            synTest.write();
        }).start();

        TimeUnit.SECONDS.sleep(2);
    }

}

切记 不能在main线程中启动,需要单独创建两个线程去执行,否则main线程阻塞的话,程序就会阻塞不会执行下去。

conditon

java 复制代码
package com.jia.suspension;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author qxlx
 * @date 2023/12/30 3:31 PM
 */
public class ConditionTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private Object obj;

    public Object read () {
        try {
            lock.lock();
            while (obj == null) {
                System.out.println("getLock");
                condition.await();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return obj;
    }

    public void write() {
        try {
            lock.lock();
            obj = new Object();
            condition.signalAll();
            System.out.println("唤醒");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionTest test = new ConditionTest();

        new Thread(()-> {
            test.read();
        }).start();

        new Thread(()-> {
            test.write();
        }).start();

        TimeUnit.SECONDS.sleep(1);
    }


}

LockSouport

java 复制代码
package com.jia.suspension;

import java.util.concurrent.locks.LockSupport;

/**
 * @author qxlx
 * @date 2023/12/30 3:38 PM
 */
public class LockSupportTest {

    private Object obj;

    public Object read() {
        while (obj == null) {
            System.out.println("read-线程等待");
            LockSupport.park();
            System.out.println("read-线程唤醒");
        }
        return obj;
    }

    public void write(Thread thread) {
        obj = new Object();
        LockSupport.unpark(thread);
        System.out.println("唤醒线程");
    }

    public static void main(String[] args) {
        LockSupportTest lockSupportTest = new LockSupportTest();

        Thread thread = new Thread(() -> {
            lockSupportTest.read();
        });

        thread.start();

        Thread thread2 = new Thread(() -> {
            lockSupportTest.write(thread);
        });

        thread2.start();
    }

}

好了以上就是三种唤醒阻塞的方式。

应用场景

  • 多线程环境下多个线程访问相同实例资源,从实例资源中获得资源并处理;
  • 实例资源需要管理自身拥有的资源,并对请求线程的请求作出允许与否的判断

在实际的开发中,我们对外提供一个API数据查询的接口,但是需要以来下游系统进行组合数据,将结果写入MQ,下游服务处理完毕后,然后另外一个线程进行获取数据处理。

从图中可以看从处理web请求的是蓝色的线程,而从Topic获取数据的线程是红色线程,也就是蓝色线程异步写入Topic数据后,会阻塞,等待红色线程获取结果后,然后在返回结果。

相关推荐
蜗牛一步步往上爬2 小时前
第十六章 C++语言在深度学习中的应用
开发语言·c++·深度学习
这个昵称不容易4 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长4 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
张张123y4 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
文丑颜不良啊5 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼5 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
冻柠檬飞冰走茶7 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
心若自由何处是束缚7 小时前
适配器模式:从原理到实战的完整指南
设计模式·适配器模式
文丑颜不良啊7 小时前
LeetCode2161~2180题目解析
java·leetcode
FRing037 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2