spring在容器中使用了观察者模式:

一、spring事件:ApplicationEvent,该抽象类继承了EventObject类,jdk建议所有的事件都应该继承自EventObject。

二、spring事件监听器:ApplicationLisener,该接口继承了EventListener接口,jdk建议所有的事件监听器都应该继承EventListener。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {  

    /**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}

三、spring事件发布:ApplicationEventPublisher 。 ApplicationContext继承了该接口,在ApplicationContext的抽象类AbstractApplicationContext中做了实现。

package org.springframework.context;

public interface ApplicationEventPublisher {  
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as RequestHandledEvent) or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
  void publishEvent(ApplicationEvent var1);

    void publishEvent(Object var1);
}

AbstractApplicationContext类中publishEvent方法实现:

    /**
* Publish the given event to all listeners.
* <p>Note: Listeners get initialized after the MessageSource, to be able
* to access it within listener implementations. Thus, MessageSource
* implementations cannot publish events.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
*/
@Override
public void publishEvent(Object event) {
publishEvent(event, null);
} /**
* Publish the given event to all listeners.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
} // Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
} // Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
       //事件广播委托给ApplicationEventMulticaster来进行  
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
} // Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

由上代码可知,AbstractApplicationContext类并没有具体的做事件广播,而是委托给ApplicationEventMulticaster来进行,ApplicationEventMulticaster的multicastEvent()方法实现如下:

    @Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
  protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
} catch (Throwable var6) {
errorHandler.handleError(var6);
}
} else {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var5) {
LogFactory.getLog(this.getClass()).debug("Non-matching event type for listener: " + listener, var5);
}
} }

获得listener集合,遍历listener触发事件Executor接口有多个实现类,可以支持同步或异步广播事件。

问题:spring容器是怎么根据事件去找到事件对应的事件监听器呢?

一、入口

private ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:/spring/applicationContext.xml");

二、生成Spring上下文ApplicationContext

   public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
} }

三、调用spring容器初始化方法

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); //初始化一个事件注册表
// Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // 初始化事件监听器
// Check for listener beans and register them.
registerListeners();
// 实例化所有单例对象,其中包括默认注册表
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 发布事件
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

3.1 initApplicationEventMulticaster()方法代码

   protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
     //先查找BeanFactory配置文件中是否有ApplicationEventMulticaster  
if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {// 如果beanFactory中没有,则创建一个SimpleApplicationEventMulticaster
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [" + this.applicationEventMulticaster + "]");
}
} }

spring先从beanFactory中获取ApplicationEventMulticaster,如果没有自定义,则创建一个SimpleApplicationEventMulticaster。

ApplicationEventMulticaster包含以下属性:defaultRetriever即为注册表,注册监听事件的相关消息; retrieverCache用来做defaultRetriever的缓存。

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
final Map<AbstractApplicationEventMulticaster.ListenerCacheKey, AbstractApplicationEventMulticaster.ListenerRetriever> retrieverCache = new ConcurrentHashMap(64);
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private Object retrievalMutex;
}

ListenerRetriever的数据结构如下:applicationListeners用来存放监听事件, applicationListenerBeans为存放监听事件的类名称。

private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
public final Set<String> applicationListenerBeans = new LinkedHashSet();
private final boolean preFiltered;

ListenerCacheKey的数据结构如下:eventType是事件类型,sourceType是事件的源类型,即为事件的构造函数的参数类型。

   private static class ListenerCacheKey {
private final ResolvableType eventType;
private final Class<?> sourceType;

3.2 registerListeners()方法代码

初始化注册表以后,则把事件注册到注册表中,registerListeners()

  protected void registerListeners() {
     //获取所有的listener的迭代器
Iterator var1 = this.getApplicationListeners().iterator(); while(var1.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var1.next();
//把获取所有的listener, 把事件的bean放到ApplicationEventMulticaster中的ApplicationListener
       this.getApplicationEventMulticaster().addApplicationListener(listener);
} String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
String[] var7 = listenerBeanNames;
int var3 = listenerBeanNames.length; for(int var4 = 0; var4 < var3; ++var4) {
String listenerBeanName = var7[var4];
  //把事件的名称放到ApplicationListenerBean里去 
      this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
} Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
Iterator var9 = earlyEventsToProcess.iterator(); while(var9.hasNext()) {
ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
} }
3.3 finishBeanFactoryInitialization(beanFactory)

 具体会执行到下面的方法,会把AbstractApplicationEventMulticaster的defaultRetriever属性赋值。

 执行PostProcessorRegistrationDelegate类的postProcessAfterInitialization()方法:
 public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.applicationContext != null && bean instanceof ApplicationListener) {
Boolean flag = (Boolean)this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
this.applicationContext.addApplicationListener((ApplicationListener)bean);
} else if (flag == null) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " + "but is not reachable for event multicasting by its containing ApplicationContext " + "because it does not have singleton scope. Only top-level listener beans are allowed " + "to be of non-singleton scope.");
} this.singletonNames.put(beanName, Boolean.FALSE);
}
} return bean;
}

  执行AbstractApplicationContext类的addApplicationListener()方法:

  public void addApplicationListener(ApplicationListener<?> listener) {
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
} else {
this.applicationListeners.add(listener);
}
}
  执行AbstractApplicationEventMulticaster类的addApplicationListener()方法
  public void addApplicationListener(ApplicationListener<?> listener) {
Object var2 = this.retrievalMutex;
synchronized(this.retrievalMutex) {
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}

【spring根据反射机制,通过方法getBeansOfType()获取所有继承了ApplicationListener接口的监听器,然后把监听器全放到注册表里,所以我们可以在spring配置文件中配置自定义的监听器,在spring初始化的时候会把监听器自动注册到注册表中。】

3.4 finishRefresh()
里面执行发布事件。
  protected void finishRefresh() {
this.initLifecycleProcessor();
this.getLifecycleProcessor().onRefresh();
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
LiveBeansView.registerApplicationContext(this);
}

在applicationContext发布事件的时候。

 public void publishEvent(ApplicationEvent event) {
this.publishEvent(event, (ResolvableType)null);
} public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
} protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Publishing event in " + this.getDisplayName() + ": " + event);
} Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
} if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 调用ApplicationEventMulticaster的multicastEvent()方法  
      this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
} if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
} }

AbstractApplicationContext类并没有具体的做事件广播,而是委托给ApplicationEventMulticaster来进行。

ApplicationEventMulticaster的方法multicastEvent()为:

 public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator(); while(var4.hasNext()) {
final ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
public void run() {
SimpleApplicationEventMulticaster.this.invokeListener(listener, event);
}
});
} else {
this.invokeListener(listener, event);
}
} }

根据事件和类型获取所有的监听器方法: getApplicationListeners()

 protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = source != null ? source.getClass() : null;
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
       //从缓存里查找ListenerRetriever    
if (retriever != null) {
return retriever.getApplicationListeners();
} else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
Object var7 = this.retrievalMutex;
synchronized(this.retrievalMutex) {
retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
} else {
            //如果缓存里不存在,则去获得 
retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
} else {
return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
}
}
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, Class<?> sourceType, AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
LinkedList<ApplicationListener<?>> allListeners = new LinkedList();
Object var7 = this.retrievalMutex;
LinkedHashSet listeners;
LinkedHashSet listenerBeans;
synchronized(this.retrievalMutex) {
         //获取注册表里所有的listener, defaultRetriever在前面已被赋值 
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
} Iterator var14 = listeners.iterator(); while(var14.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var14.next();
       //根据事件类型,事件源类型,获取所需要的监听事件  
if (this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
} allListeners.add(listener);
}
} if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = this.getBeanFactory();
Iterator var16 = listenerBeans.iterator(); while(var16.hasNext()) {
String listenerBeanName = (String)var16.next(); try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || this.supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
} allListeners.add(listener);
}
}
} catch (NoSuchBeanDefinitionException var13) {
;
}
}
} AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}

配合上面的注解,即可理解,根据事件和事件类型找到对应的监听器,那么如何根据事件类型找到对应的监听器呢?

上面方法中的supportsEvent(listener, eventType, sourceType)方法实现了根据事件类型查找对应的监听器,代码具体实现为:

  protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
GenericApplicationListener smartListener = listener instanceof GenericApplicationListener ? (GenericApplicationListener)listener : new GenericApplicationListenerAdapter(listener);
return ((GenericApplicationListener)smartListener).supportsEventType(eventType) && ((GenericApplicationListener)smartListener).supportsSourceType(sourceType);
}

如上可知:上面方法的返回结果跟方法smartListener.supportsEventType(eventType)和smartListener.supportsSourceType(sourceType)有关。

smartListener.supportsEventType(eventType)方法实现为:

public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof SmartApplicationListener) {
Class<? extends ApplicationEvent> eventClass = eventType.getRawClass();
return ((SmartApplicationListener)this.delegate).supportsEventType(eventClass);
} else {
return this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType);
}
}

该方法主要的逻辑就是根据事件类型判断是否和监听器参数泛型的类型是否一致。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {  

    /**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event); }

在定义自己的监听器要明确指定参数泛型,表明该监听器支持的事件,如果不指明具体的泛型,则没有监听器监听事件。

smartListener.supportsSourceType(sourceType)方法的实现为:

public boolean supportsSourceType(Class<?> sourceType) {
return this.delegate instanceof SmartApplicationListener ? ((SmartApplicationListener)this.delegate).supportsSourceType(sourceType) : true;
}

以上是spring的事件体系。

四、自定义事件和监听器

我们可以使用spring的事件广播体系,自定义自己的事件:

自定义事件,继承ApplicationEvent:

public class DIYEvent extends ApplicationEvent {
private static final long serialVersionUID = 7099057708183571977L; public DIYEvent(String source) {
super(source);
}
}

自定义listener,继承ApplicationListener

@Component
public class DIYListener implements ApplicationListener<DIYEvent> {
@Override
public void onApplicationEvent(DIYEvent diyEvent) {
System.out.println("自定义监听器执行");
System.out.println(diyEvent.getSource());
}
}

测试触发事件:

public class DIYTest{
private ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:/spring/applicationContext.xml"); @Test
public void diyTest(){
applicationContext.publishEvent(new DIYEvent("测试数据"));
}
}

获取ApplicationContext,发布事件。

调试结果:

自定义监听器执行
测试数据

Spring中的设计模式--观察者模式的更多相关文章

  1. JDK和Spring中的设计模式

    创建型 1)工厂方法 Collection.iterator() 由具体的聚集类来确定使用哪一个Iterator 2)单例模式 Runtime.getRuntime() 3)建造者模式 StringB ...

  2. Spring中的设计模式

    [Spring中的设计模式] http://www.uml.org.cn/j2ee/201301074.asp [详解设计模式在Spring中的应用]    [http://www.geek521.c ...

  3. spring 中的设计模式

    https://mp.weixin.qq.com/s?__biz=MzU0MDEwMjgwNA==&mid=2247485205&idx=1&sn=63455d2313776d ...

  4. [Head First设计模式]山西面馆中的设计模式——观察者模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 引言 不知不自觉又将设计模式融入生活了,吃个饭也不得安生,也发现生活中的很多场景,都可以用设计模式来模拟.原来设计模式就在 ...

  5. Spring中的设计模式学习

    Spring提供了一种Template的设计哲学,包含了很多优秀的软件工程思想. 1. 简单工厂 又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一. ...

  6. Spring中的设计模式:模板模式

    导读 模板模式在是Spring底层被广泛的应用,比如事务管理器的实现,JDBC模板的实现. 文章首发于作者的微信公众号[码猿技术专栏] 今天就来谈谈「什么是模板模式」.「模板模式的优缺点」.「模板模式 ...

  7. Spring中的设计模式2

    Spring设计模式分析   工厂模式和单态模式 工厂模式:可以将java对象对象的调用者从被调用者的实现逻辑中分离.调用者只关心被调用者必须满足的某种规则,这种规则我们看做是接口,不必关心实例的具体 ...

  8. Spring中的设计模式:工厂方法模式

    导读 工厂方法模式是所有设计模式中比较常用的一种模式,但是真正能搞懂用好的少之又少,Spring底层大量的使用该设计模式来进行封装,以致开发者阅读源代码的时候晕头转向. 文章首发于微信公众号[码猿技术 ...

  9. [工作中的设计模式]观察者模式observer

    一.模式解析 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观察者模式又叫订阅发布模式, ...

随机推荐

  1. day1 python 介绍、基本语法、流程控制

    请查看我的云笔记链接: http://note.youdao.com/noteshare?id=0ea7425d3e3669800cb0d73f7ec8865d&sub=D87B4BF820C ...

  2. 诊断GRID无法启动问题

    诊断 Grid Infrastructure 启动问题 (文档 ID 1623340.1)

  3. May 19th 2017 Week 20th Friday

    Good luck is another name for tenacity of purpose. 好运不过是坚持不懈的结果. To some extent, I don't agree with ...

  4. Ubuntu 下安装Mongodb

    Mongodb是一款开源的数据库,这里不用我多说了,下面说一下Ubuntu下安装Mongodb可能遇到的问题和解决方案. 故事背景: 今天M$促销,1¥Windows Azure 4000¥-30天的 ...

  5. 真的有用吗?(GitHub)

    为什么要新建一个GitHub账号   一个程序员不知道GitHub,那我就笑笑,呵呵哒.什么是GitHub呢? 就我知道的git,谈一下.Git是一个版本控制软件,这个软件最初是Linux之父林纳斯. ...

  6. C4C Cloud Application Studio做ABSL开发的一些性能方面的最佳实践

    Stefan Hagen在博文SAP Cloud Application Studio Performance Best Practices里介绍了在C4C里使用Cloud Application S ...

  7. Android(java)学习笔记14:Java线程池

    1. 线程池: 1)程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互.而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池. 2)线程池里 ...

  8. 【洛谷5280】[ZJOI2019] 线段树(线段树大力分类讨论)

    点此看题面 大致题意: 给你一棵线段树,两种操作.一种操作将每棵线段树复制成两个,然后在这两个线段树中的一个上面进行\(Modify(l,r)\).另一种操作询问所有线段树的\(tag\)总和. 大力 ...

  9. BZOJ3874:[AHOI2014&JSOI2014]宅男计划(爬山法)

    Description  [故事背景] 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY 不得不依靠叫外卖来维持生计. [问题描述] 外卖店一共有N种食物,分别有1到N编号.第i种 ...

  10. hyper-net、ion、skip connection、fpn

    resnet的skip connection用的也是eltwise相加 fpn的浅层和高层融合用的eltwise相加 hyper-net和ion都是使用的concat的方式 hyper-net网络结构 ...