EventListener与EventBus
EventListener
JDK
JDK1.1开始就提供EventListener,一个标记接口,源码如下:
/*** A tagging interface that all event listener interfaces must extend.*/
public interface EventListener {
}
JDK提供的java.util.EventObject:
public class EventObject implements Serializable {protected transient Object source;
}
用户需要定义继承自EventObject的事件对象,然后定义具体的Listener接口继承EventListener。事件源通过添加监听器(addXXXListener方法)手动管理事件的订阅和发布,需要用户手动调用监听器的回调方法,事件管理逻辑往往由开发者编码完成。
典型场景:主要用于早期GUI应用开发(如AWT和Swing),用于组件间的事件通信。
优点:轻量级,易于理解;适合简单场景或单一模块。
缺点:
- 需显式注册和管理事件监听器,手动处理事件流转,容易导致代码复杂度增加;
- 缺乏高级特性,如事件异步处理、条件过滤等。
Spring
@EventListener
当在实现某些特定业务逻辑时,通常可通过发送事件的方式实现解耦,这也是观察者模式的一种体现。从Spring 4.2开始提供注解@EventListner,不再需要单独编写监听器类,只需在Spring Bean方法上标记@EventListener即可。
源码如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {@AliasFor("classes")Class<?>[] value() default {};@AliasFor("value")Class<?>[] classes() default {};String condition() default "";
}
解读:
- value和classes:作用相同,表示监听的一个或一组事件,用于支持方法中同一个父类的事件;
- condition:支持Spring EL表达式,用来做Event中的变量或者方法判断。
Event的整个生命周期,从Publisher发出,经过applicationContext容器通知到EventListener,都是发生在单个Spring容器中。
事件可直接使用ApplicationEvent,或继承它。源码如下:
public abstract class ApplicationEvent extends java.util.EventObject {// 事件发生时间private final long timestamp;public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public ApplicationEvent(Object source, Clock clock) {super(source);this.timestamp = clock.millis();}
}
示例
多个监听器,监听Account创建,完成不同的业务逻辑。
创建Event事件监听
/*** 账号监听,处理账号创建成功的后续逻辑*/
@Component
public class AccountListener {@EventListener@Asyncpublic void processAccountCreatedEvent1(AccountCreatedEvent event) {// 1. 发送邮件、短信}@EventListener@Order(100)public void processAccountCreatedEvent2(AccountCreatedEvent event) {// 2. 添加积分等,@Order(100)用来设定执行顺序}
}
使用ApplicationEventPublisher发送事件:
@Autowired
private ApplicationEventPublisher publisher;public boolean save(Account account) {// 数据库保存成功if (true) {publisher.publishEvent(new AccountCreatedEvent(account));}return false;
}
一个发布者可对应多个监听者:
@EventListener(value = {AccountCreatedEvent.class, AccountUpdatedEvent.class}, condition = "#event.account.age > 10")
public void processAccountCreatedEvent2(AccountEvent event) {// 业务逻辑
}
监听执行顺序:可使用@Order(100)来标记事件的执行顺序,异步情况下只保证按顺序将监听器丢入进线程池,具体执行顺序得看线程。
监听异步执行:使用@Async标记即可,前提条件:使用@EnableAsync开启Spring异步。
优点:
- 更加简洁,方法注解即声明监听;
- 支持异步处理,条件过滤,功能更强大;
- 与其他Spring特性(如AOP和事务)集成更好。
缺点:
- 不如ApplicationListener明确(可能难以追踪监听器逻辑);
- 依赖注解,可能不适合部分代码风格偏向接口设计的团队。
ApplicationListener
Spring提供两种事件监听机制:
- 基于@EventListener注解:
- 基于ApplicationListener泛型接口:早期提供,实现onApplicationEvent方法
通过上下文来发布一个事件,监听器收到订阅的事件作相应的处理,Spring提供的方便高效的事件驱动模型。
ApplicationListener源码:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends java.util.EventListener {void onApplicationEvent(E event);default boolean supportsAsyncExecution() {return true;}static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {return event -> consumer.accept(event.getPayload());}}
简单使用:
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("Received event: " + event.getMessage());}
}
优点:
- 明确,适合固定、简单的监听逻辑;
- 提供对所有事件的统一入口,可基于事件类型实现复杂的分发逻辑。
缺点:
- 相对冗长,代码侵入性高(必须实现接口);
- 不支持直接使用条件过滤和异步处理;
- 扩展性和灵活性低。
@EventListener与ApplicationListener
| 特性 | ApplicationListener | @EventListener |
|---|---|---|
| 定义方式 | 实现ApplicationListener接口 | 使用@EventListener注解 |
| 写法复杂度 | 必须实现接口 | 注解方式更简洁 |
| 类型绑定 | 泛型绑定到特定事件类型 | 自动通过方法参数类型推断 |
| 条件过滤 | 不支持 | 支持(condition属性) |
| 异步处理 | 需手动实现多线程 | 支持@Async开箱即用 |
| 事务集成 | 支持,但需手动配置 | 与Spring的事务注解自然集成 |
| 适用场景 | 简单、固定事件监听逻辑 | 更现代化、复杂事件管理逻辑 |
| 引入版本 | 早期版本已有 | Spring 4.2+ |
JDK与Spring
| 特性 | JDK EventListener | Spring @EventListener |
|---|---|---|
| 触发方式 | 显式调用监听器 | 基于事件发布自动触发 |
| 实现方式 | 通过接口 | 通过注解声明 |
| 事件管理 | 手动注册、调用 | 自动管理(基于Spring容器) |
| 适用场景 | 简单GUI或模块内部通信 | 企业级应用、跨模块解耦 |
| 异步支持 | 不支持 | 支持 |
| 条件过滤 | 不支持 | 支持 |
| 事务集成 | 不支持 | 支持 |
| 复杂性 | 简单,手动管理 | 较高,但更自动化和灵活 |
EventBus
使用异步的方式来发送事件,或触发另外一个动作,即Publish/Subscribe Event。
EventBus有不同的实现框架,一般都指Guava EventBus。
框架
Guava EventBus包路径下:

类之间的关系如:

EventBus组成部分:
- EventBus、AsyncEventBus:事件发送器
- Event:事件承载单元
- SubscriberRegistry:订阅者注册器,将订阅者注册到Event上,即将有注解Subscribe的方法和Event绑定起来
- Dispatcher:事件分发器,将事件的订阅者调用来执行
- Subscriber、SynchronizedSubscriber:订阅者,并发订阅还是同步订阅
原理
EventBus是基于注册监听的方式来运行的,首先需将EventBus实例化,然后才会有事件及监听者:
// 同步
EventBus eventBus = new EventBus();
注册监听者,底层就是将类eventListener中所有注解有Subscribe的方法与其Event对放在一个map中(一个Event可以对应多个Subscribe的方法):
eventBus.register(eventListener);
在高并发的环境下使用AsyncEventBus时,发送事件可能会出现异常,因为它使用的线程池,当线程池的线程不够用时,会拒绝接收任务,就会执行线程池的拒绝策略,如果需要关注是否提交事件成功,就需要将线程池的拒绝策略设为抛出异常,并且try-catch来捕获异常:
try {eventBus.post(new LoginEvent("user", "pass"));
} catch (Exception e) {// log
}
内部的订阅通知模型,无需使用事件+事件listener模型,只有一个事件类。
示例
一个简单的实例,值得一提的是,注册和发布事件,与消费事件不在一个类里:
// 事件类中方法以@Subscribe注解
class EventBusChangeRecorder {@Subscribepublic void recordCustomerChange(ChangeEvent e) {// 业务逻辑System.out.println("事件触发");}
}
// 创建事件总线
EventBus eventBus = new EventBus();
// 注册事件
eventBus.register(new EventBusChangeRecorder());
ChangeEvent event = new ChangeEvent(new EventBusChangeRecorder());
// 发布事件
eventBus.post(event);
// 需要异步执行可使用EventBus的子类AsyncEventBus
另外,EventBus也可作为Spring Bean使用,可被注入:
@Service
public class TestService implements InitializingBean {@Resourceprivate MyListener myListener;@Resourceprivate EventBus eventBus;public void postEvent() {eventBus.post(new LoginEvent("johnny", "success"));}@Overridepublic void afterPropertiesSet() throws Exception {eventBus.register(myListener);}
}
源码
EventBus的register方法:
void register(Object listener) {Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {Class<?> eventType = entry.getKey();Collection<Subscriber> eventMethodsInListener = entry.getValue();CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);if (eventSubscribers == null) {CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();eventSubscribers = MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);}eventSubscribers.addAll(eventMethodsInListener);}
}
事件发送:执行指定事件类型的订阅者(包含method),从订阅者中获取指定事件的订阅者,然后按照规则(同步、异步)执行指定的方法,如果事件没有监听者,就当作死亡事件来对待。EventBus的post方法:
public void post(Object event) {Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);if (eventSubscribers.hasNext()) {dispatcher.dispatch(event, eventSubscribers);} else if (!(event instanceof DeadEvent)) {// the event had no subscribers and was not itself a DeadEventpost(new DeadEvent(this, event));}
}
EventBus的dispatcher为PerThreadQueuedDispatcher,其dispatch方法如下:
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {checkNotNull(event);checkNotNull(subscribers);Queue<Event> queueForThread = queue.get();queueForThread.offer(new Event(event, subscribers));if (!dispatching.get()) {dispatching.set(true);try {Event nextEvent;while ((nextEvent = queueForThread.poll()) != null) {while (nextEvent.subscribers.hasNext()) {nextEvent.subscribers.next().dispatchEvent(nextEvent.event);}}} finally {dispatching.remove();queue.remove();}}
}
dispatchEvent方法:
// Dispatches event to this subscriber using the proper executor.
final void dispatchEvent(final Object event) {executor.execute((Runnable) () -> {try {invokeSubscriberMethod(event);} catch (InvocationTargetException e) {bus.handleSubscriberException(e.getCause(), context(event));}});
}
execute方法由Executor来执行。EventBus的executor为MoreExecutors.directExecutor():
public static Executor directExecutor() {return DirectExecutor.INSTANCE;
}enum DirectExecutor implements Executor {INSTANCE;@Overridepublic void execute(Runnable command) {command.run();}
}
其execute方法直接执行线程的run方法,即同步调用run方法。另外,invokeSubscriberMethod方法如下:
void invokeSubscriberMethod(Object event) throws InvocationTargetException {try {method.invoke(target, checkNotNull(event));} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {// 省略catch代码}
}
因此,整个执行过程如下:

整个过程都是同步方式执行,EventBus是同步的。
AsyncEventBus
AsyncEventBus是异步EventBus,其dispatcher为LegacyAsyncDispatcher,executor为自己指定的线程池,如
@Configuration
public class ConfigBean {@Beanpublic EventBus executorService() {BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(20);ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 30, TimeUnit.SECONDS, workQueue);return new AsyncEventBus(executor);}
}
运行流程如下:

AllowConcurrentEvents
Guava提供的注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ElementTypesAreNonnullByDefault
public @interface AllowConcurrentEvents {
}
注解判断逻辑位于Subscriber.isDeclaredThreadSafe方法内:
private static boolean isDeclaredThreadSafe(Method method) {return method.getAnnotation(AllowConcurrentEvents.class) != null;
}
此方法被create()方法调用:
static Subscriber create(EventBus bus, Object listener, Method method) {return isDeclaredThreadSafe(method)? new Subscriber(bus, listener, method): new SynchronizedSubscriber(bus, listener, method);
}
解读:如果订阅者方法上有注解@AllowConcurrentEvents,则返回Subscriber(异步),否则,返回SynchronizedSubscriber(同步)。即没有使用注解AllowConcurrentEvents的订阅者,在并发环境中是串行执行,会影响性能。
SynchronizedSubscriber是同步的,从其类名可知,从其实现的invokeSubscriberMethod方法里也可看出:
@Override
void invokeSubscriberMethod(Object event) throws InvocationTargetException {synchronized (this) {super.invokeSubscriberMethod(event);}
}
拓展
EventListener和EventBus
相同点:都是基于事件驱动模式,都可用于实现解耦的组件通信。
不同点:
- 功能:
- EventListener:订阅事件并执行操作的组件;粒度更细,聚焦于局部业务逻辑;一般是自定义类或方法,绑定到特定事件类型;
- EventBus:管理事件的发布、订阅、流转;侧重于全局管理事件,通常是单例模式或共享对象。
- 实现方式:
- EventListener:Spring的@EventListener是一种更广义的事件监听机制,可配合ApplicationEvent或自定义事件;
- EventBus:Guava EventBus通过注解定义事件处理器,自动绑定到特定事件类型。
联系:EventListener通常依赖EventBus进行注册,接收由EventBus发布的事件。
EventBus对比MQ
| 特性 | EventBus | MQ |
|---|---|---|
| 使用范围 | 单JVM | 跨进程、分布式 |
| 通信模式 | 内存中发布-订阅 | 发布-订阅、点对点 |
| 可靠性 | 无内置持久化或重试机制 | 支持持久化、重试、消息确认 |
| 延迟 | 低延迟 | 相对稍高(取决于网络和队列机制) |
| 复杂性 | 简单,适合轻量场景 | 较高,需要额外运维 |
Guava EventBus现状
- 局限性:
基于内存的轻量级工具,仅适用于单JVM内的事件管理;在分布式跨服务场景下,无法满足可靠性、持久性和扩展性要求。 - 依然适用的场景:
- 轻量化需求:在小型项目中,快速实现解耦的优秀选择;
- 单JVM内部事件管理:用于模块间事件流转(如前端事件或服务内异步操作);
- 开发测试:在测试环境中模拟事件驱动的逻辑。
Netflix EventBus
除了Guava EventBus外,Netflix也提供一套框架:
<dependency><groupId>com.netflix.netflix-commons</groupId><artifactId>netflix-eventbus</artifactId><version>0.3.0</version>
</dependency>
参考
- EventBus原理深度解析
相关文章:
EventListener与EventBus
EventListener JDK JDK1.1开始就提供EventListener,一个标记接口,源码如下: /*** A tagging interface that all event listener interfaces must extend.*/ public interface EventListener { }JDK提供的java.util.EventObject࿱…...
Facebook为什么注册失败了?该怎么解决?
有时候用户在尝试注册Facebook账号时可能会遇到各种问题,导致注册失败或遇到困难。小编会为大家分析Facebook注册失败的可能原因,并提供解决方法,帮助大家顺利完成注册流程。 一、Facebook注册失败的可能原因 1. 账号信息问题: …...
前端数据可视化思路及实现案例
目录 一、前端数据可视化思路 (一)明确数据与目标 (二)选择合适的可视化图表类型 (三)数据与图表的绑定及交互设计 (四)页面布局与样式设计 二、具体案例:使用 Ech…...
【DVWA】Brute Force暴力破解实战
问尔辈 何等样人 自摸心头 再来求我;若汝能 克存忠孝 持身正直 不拜何妨 1.Brute Force(Low) 相关的代码分析 if( isset( $_GET[ Login ] ) ) {// Get username$user $_GET[ username ];// Check the database$query "SELECT * FROM users WHERE user $…...
23种设计模式速记法
前言 在软件开发的过程中,设计模式作为解决常见问题的通用模板,一直是开发者的重要工具。尤其是在面临复杂系统架构和需求变化时,设计模式不仅能够提升代码的可复用性和扩展性,还能大大提高团队之间的协作效率。然而,…...
第7章硬件测试-7.3 功能测试
7.3 功能测试 7.3.1 整机规格测试7.3.2 整机试装测试7.3.3 DFX测试 功能测试包括整机规格、整机试装和整机功能测试,是整机结构和业务相关的测试。 7.3.1 整机规格测试 整机规格测试包括尺寸、重量、温度、功耗等数据。这些测试数据与设计规格进行比对和校验&…...
动态规划子数组系列一>等差数列划分
题目: 解析: 代码: public int numberOfArithmeticSlices(int[] nums) {int n nums.length;int[] dp new int[n];int ret 0;for(int i 2; i < n; i){dp[i] nums[i] - nums[i-1] nums[i-1] - nums[i-2] ? dp[i-1]1 : 0;ret dp[i…...
《Python浪漫的烟花表白特效》
一、背景介绍 烟花象征着浪漫与激情,将它与表白结合在一起,会创造出别具一格的惊喜效果。使用Python的turtle模块,我们可以轻松绘制出动态的烟花特效,再配合文字表白,打造一段专属的浪漫体验。 接下来,让…...
什么是RESTful API,有什么特点
RESTful API 概述 什么是 RESTful API? RESTful API 是基于 Representational State Transfer(表现层状态转移)架构风格的 Web 服务接口。REST 是一种设计风格,而不是具体的协议或标准。它定义了一组约束和最佳实践,…...
友思特新闻 | 友思特荣获广州科技创新创业大赛智能装备行业赛初创组优胜企业!
2024年11月19日,第十三届中国创新创业大赛(广东广州赛区)暨2024年广州科技创新创业大赛智能装备行业赛颁奖典礼隆重举行。 赛事奖项介绍:广州科技创新创业大赛智能装备行业赛 第十三届“中国创新创业大赛(广东广州赛区…...
CSS中calc语法不生效
问题起因 在使用calc时发现无法生效,写法是: height:calc(100vh-100px);页面无效果,加空格后就发现有效果了: height:calc(100vh - 100px);这是为什么? calc是什么? css3 的计算属性,用于动态…...
国标GB28181视频平台EasyCVR视频融合平台H.265/H.264转码业务流程
在当今数字化、网络化的视频监控领域,大中型项目对于视频监控管理平台的需求日益增长,特别是在跨区域、多设备、高并发的复杂环境中。EasyCVR视频监控汇聚管理平台正是为了满足这些需求而设计的,它不仅提供了全面的管理功能,还支持…...
ES6 模板字符串详解
ES6 模板字符串详解 ES6(ECMAScript 6)引入了模板字符串(Template Literals),这是一种新的字符串字面量语法,使用反引号()来定义字符串。模板字符串不仅支持多行字符串,…...
浏览器插件启动本地程序
浏览器插件支持启动本地程序,且支持win、mac、linux多个平台,使用的是nativeMessaging。nativeMessaging官方api说明。nativeMessaging支持启动本地程序且进行通信。 我们直接拿官方提供的例子进行说明,github地址。 以win为例 1、添加注册…...
Ubuntu ESP32开发环境搭建
文章目录 ESP32开发环境搭建安装ESP-IDF搭建一个最小工程现象 ESP32开发环境搭建 最近有个小项目需要用到能够联网的mcu驱动,准备玩玩esp的芯片,记录下ESP32开发环境搭建的过程。 ESP-IDF 是乐鑫科技为其 ESP32 系列芯片提供的官方开发框架。这个框架主…...
【gitlab】部署
直接RPM安装 部署的方式是:使用外部的nginx作为代理,使用https方式。 1、下载安装文件 gitlab-ce-17.0.3-ce.0.el7.x86_64.rpm 2、安装 yum install gitlab-ce-17.0.3-ce.0.el7.x86_64.rpm 或者安装yum源在线安装: 添加镜像源:新建 /et…...
vue中路由缓存
vue中路由缓存 问题描述及截图解决思路关键代码及打印信息截图 问题描述及截图 在使用某一平台时发现当列表页码切换后点击某一卡片进入详情页后,再返回列表页时页面刷新了。这样用户每次看完详情回到列表页都得再重新输入自己的查询条件,或者切换分页到…...
Github 2024-11-18 开源项目周报 Top15
根据Github Trendings的统计,本周(2024-11-18统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目8TypeScript项目4Jupyter Notebook项目2Go项目2Shell项目1OpenHands: 人工智能驱动的软件开发代理平台 创建周期:195 天开发语言:P…...
GRCNN使用onnxruntime和tensorrt推理
下载GRCNN项目:https://github.com/skumra/robotic-grasping.git 导出onnx模型: import torchnet torch.load("trained-models/jacquard-rgbd-grconvnet3-drop0-ch32/epoch_42_iou_0.93") x torch.rand(1, 4, 300, 300).cuda() torch.onnx.…...
java中的this关键字
🎉🎉🎉欢迎来到我的博客,我是一名自学了2年半前端的大一学生,熟悉的技术是JavaScript与Vue.目前正在往全栈方向前进, 如果我的博客给您带来了帮助欢迎您关注我,我将会持续不断的更新文章!!!🙏🙏🙏 文章目录…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
