public synchronized void unregister(Object subscriber) {
List> subscribedTypes = typesBySubscriber.get(subscriber); //通过订阅类获取他当中的所有订阅事件类,我们加入的时候时加入事件类的class
if (subscribedTypes != null) {
for (Class eventType : subscribedTypes) { //遍历订阅事件,开始解绑另外一个集合中的数据:以eventType为key中删除
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber); //从当前订阅类为key的map移除
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class eventType) {
List subscriptions = subscriptionsByEventType.get(eventType); //获取以当前事件为key的所有订阅方法集合
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) { //遍历如果事件是在当前类中订阅的,则从订阅方法集合中删除,因为这个类就要over啦,自然也就不用再接受订阅事件啦
subscription.active = false; //标记不激活
subscriptions.remove(i); //从订阅方法集合中删除
i--;
size--;
}
}
}
}
post发送事件
通过上面的注册和反注册已经将事件,订阅方法都添加到集合中了,我们就可以发送一个事件开始响应方法啦!
小知识点: ThreadLocal是线程隔离的只有同一线程才能获取值,通过get访问,其他线程获取不到的,同一个线程中才能获取,且同一个线程获取的是同一个对象
//ThreadLocal 的get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //如果已经存在map则返回map中的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//调用initialValue创建一个对象值,存储到map中,下次直接通过get即可获取
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//上面简单分析了ThreadLocal的源码,下面开始真正的post操作
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue() { //如果调用get方法没有value值通过这个方法新建一个返回的,下次改线程在调用,已经存在直接返回了,达到了对象的线程唯一性
return new PostingThreadState();
}
};
public void post(Object event) {
//代码很短小,第一行就蒙蔽了,不急,就是上面我们说了get函数
PostingThreadState postingState = currentPostingThreadState.get(); //不同线程对应不同的postingState,相同的线程之间遵循happended-before
List eventQueue = postingState.eventQueue; //postingState中的eventQueue队列
eventQueue.add(event); //将事件添加到队列中:每个post线程的队列都是相互独立的
//isPosting默认false,可以进去
if (!postingState.isPosting) { //相同线程是遵循happended-before规则的,是安全的
//是否是主进程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true; //阻挡不要在进来了,表示当前线程正在处理发送事件
if (postingState.canceled) { //没有别取消事件
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) { //轮询开始发送事件
postSingleEvent(eventQueue.remove(0), postingState); //队列,先进先出
}
} finally {
postingState.isPosting = false; //完成恢复默认可以在接受发送信息
postingState.isMainThread = false;
}
}
}
//静态内部类:每个线程独一份
final static class PostingThreadState {
//事件队列
final List eventQueue = new ArrayList();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
post流程
首先通过ThreadLocal获取当前线程中状态PostingState,注意不同的线程拥有不同的状态PostingState
将发送的event加入到PostingState类中的队列中
循环遍历事件队列,发送单个事件调用
发送单个Event的方法postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class eventClass = event.getClass(); //获取事件类型
boolean subscriptionFound = false;
if (eventInheritance) { //如果允许事件继承,默认允许
//找到eventClass的所有父类和实现接口
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class clazz = eventTypes.get(h);
// 依次向 eventClass 的父类或接口的订阅方法发送事件
// 只要有一个事件发送成功,返回 true ,那么 subscriptionFound 就为 true
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//不允许继承,那么只发送该事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) { //如果当前没有订阅者订阅
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
//默认允许的
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event)); //发送NosubSribeEvent事件,可以重写订阅方法接受到从而做自己的处理逻辑
}
}
}
//postSingleEventForEventType(event, postingState, eventClass)参数: 事件对象里面可能有值, 当前线程的状态postingState, 事件类型class
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass); //获取以当前事件为key的订阅类的集合(属性: 订阅类,订阅方法,是否激活)
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) { //还记得添加由大->小,这里取值是由0->size实现优先级,同时event对象不可变,但是每个共用同一个event,里面的属性值是可以更改的
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//发送事件,参数subscription订阅类信息,event发送事件对象, 是否是主线程
postToSubscription(subscription, event, postingState.isMainThread);
//是否被取消了
aborted = postingState.canceled;
} finally {
postingState.event = null; //回复默认值
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) { // 如果被取消,则跳出循环
break;
}
}
return true; //有一个发送成功即可,对应上面的判断
}
return false;
}
//postToSubscription: isMainThread 表示发送方法是否在主线程中
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) { //接受事件的订阅方法所在的线程
case POSTING: //那个线程发送就在那个线程接受
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event); //在主线程中接受
} else { //其他线程向主线程发送添加到队列(链表)后通过Handler一次发送队列中的值给UI线程最终调用的还是invokeSubscriber(subscription, event)方法
//PendingPost添加队列获取是通过PendingPost对象池,同Message相同的
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) { //如果在子线程中发送,则随机开辟一条子线程接收
backgroundPoster.enqueue(subscription, event);
} else { //在子线程中发送,在相同的线程中接收
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //无论是UI还是子线程,都会开辟一个新的线程接收
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
//invokeSubscriber参数: subscription订阅类信息: 订阅类,方法,是否激活信息
void invokeSubscriber(Subscription subscription, Object event) {
try {
//反射调用方法: method.invoke(class, 参数信息)即可,在哪个线程调用就运行在哪个线程
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause()); //如果没有找到就发送错误事件,重写即可监听到
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
主线程的mainThreadPoster.equeue()
//在EventBus中创建的
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
final class HandlerPoster extends Handler {
private final PendingPostQueue queue; //事件队列
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue(); //每个线程都有他自己的时间队列
}
void enqueue(Subscription subscription, Object event) {
//获取很有意思,所有线程共用同一个对象池,当存在可用返回,否则新建,回收的时候添加到池中,了解过Message消息对象的很熟悉啦
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost); //加入队列
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) { //发送事件封装类
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost); //UI线程取出来调用invokeSubscriber方法
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}
//Event中的方法
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost); //回收对象到对象池中以便下一个运用
if (subscription.active) { //如果当前激活状态,则反射调用方法即可
invokeSubscriber(subscription, event);
}
}
//类的静态常量,所有线程共用同一个
private final static List pendingPostPool = new ArrayList();
直到现在我们还没有用到线程池的概念吧,这个时候你是不是都忘记了EventBus构造函数中创建了一个线程池newCacheExecutor() 可能导致OOM,看一下backgroundPoster.enqueue(subscription, event);
//EventBus构造函数中创建
backgroundPoster = new BackgroundPoster(this);
final class BackgroundPoster implements Runnable { //代码量不多,整个拿过来
private final PendingPostQueue queue; //时间队列
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//通过事件封装对象池获取对象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost); //添加一个到队列中,下面就可以执行了
if (!executorRunning) { //线程池是否正在运行
executorRunning = true;
//调用EventBus中创建的线程池执行该类的run方法
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) { //双重检索队列中还没有新加则退出,开始回收线程
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
//线程池的一个线中程执行方法,同上方的Ui线程一致的,反射调用执行方法即可,无非就是运行线程不同,这个是子线程,上面的是UI线程
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
// asyncPoster.enqueue(subscription, event);同样是在EventBus构造方法中创建的
class AsyncPoster implements Runnable { //类比较简单
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll(); //通过子线程直接运行即可
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
通过上面分析,equeue添加是一个个添加,一个个执行,通过executorRunning变量控制着,如果run执行中耗时过长,添加的特别迅速,线程池将会创建大量线程,最终可能OOM
Posting: 表示post线程在哪儿发送,接受的就是该线程
Main: 如果在主线程中发送,则直接执行接受,否则利用Handler回调到主线程中执行
BackGround子线程: 如果发布事件在主线程,则调用线程池中的一个子线程去执行,否则直接在发送线程中执行
(ASYNC)异步线程: 无论发布事件是UI还是子线程都利用一个异步线程来执行!
注意:以上所有的方法分析都是在Post()方法内部调用逻辑,所以线程切换需要注意是否是线程池创建还是原来的post方法所在的线程之内
配置索引
EventBus3.0以后为了提高效率避免在运行期通过反射来做以上大量的工作,使用了索引配置生成类,可以在编译器生成注册文件,从而提升效率
使用:两种方式:使用android-apt三方插件,或者annotationProcessor
//1.使用android-apt
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "com.monster.android.wild.MyEventBusIndex"
}
}
//2. 使用annotationProcessor
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex:'com.monster.android.wild.MyEventBusIndex']
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
com.monster.android.wild.MyEventBusIndex就是我们想要生成的Subscriber Index类
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX; //缓存mao:key当前订阅类 , 值是封装的SimpleSubscriberInfo类(订阅类,应该检查父类,该订阅类中对应的订阅方法的数组)
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
// 从以上createInfoIndexFile()的实现来看,除了类名MyEventBusIndex,只有这一段代码是非hardcode。编译器自动生成的代码
// 以订阅者类为单位,将该订阅者类中的所有订阅函数以及相关参数,封装到SimpleSubscriberInfo类对象中,
// 以供EventBus在注册过程中使用。注意SimpleSubscriberInfo类对象是在编译时期就生成的,
// 因而在运行时期可以直接使用,省去了运行时期通过反射做相似操作的时间和资源消耗,从而提高效率,这里就是全文的精髓所在。
putIndex(new SimpleSubscriberInfo(com.monster.android.wild.myeventbusdemo.MainActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent",
com.monster.android.wild.myeventbusdemo.MainActivity.EventBusEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
// 将会在EventBus在注册过程中使用,等会大家会看到
@Override
public SubscriberInfo getSubscriberInfo(Class subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
上面register中有两个分支,通过ignoreGeneratedIndex是否直接使用反射调用区分,我们当时走的是第一个findUsingReflection,下面看添加索引后的第二个方法findUsingInfo
private List findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass); //findstate数组中准备并初始化FindState对象
while (findState.clazz != null) {
//重点,重点,重点: 通过索引获取编译器生成的订阅者信息
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) { //注意这里的subscriberInfo实际上是系统自动生成的类SimpleSubscriberInfo对象 //如果找到了就遍历订阅类中的订阅方法数组加入进去
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //调用SimpleSubscriberInfo.getSubscriberMethods方法获取订阅方法集合封装成SubscriberMethod[]
for (SubscriberMethod subscriberMethod : array) { //轮询,下面就跟反射是一致的操作了
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else { //否则还是老实的回到反射注册上面去吧
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState); //回收findState对象,以便下次使用
}
//getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { //新建的findState为null
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//subscriberInfoIndexes是在合适创建的
if (subscriberInfoIndexes != null) { //subscriberInfoIndexes可能添加多个文件信息,这里我们只是一个
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
//拿到唯一的一个index类即index为: MyEventBusIndex类对象
SubscriberInfo info = index.getSubscriberInfo(findState.clazz); //调用它的getSubscriberInfo(订阅类)即可获得已经put进缓存SUBSCRIBER_INDEX中的方法封装类SimpleSubscriberInfo
if (info != null) {
return info;
}
}
}
return null;
}
//通过EventBus构造函数中创建通过build传递进来的,如果我们使用索引并且重写build方法传递进来数据了
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//将索引值穿进去
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
至此:无论是直接反射注册还是通过添加索引在编译器生成文件运行期直接读取注册都分析完成了,不知道你明白了没有呢?欢迎下方留言一起讨论!
你可能感兴趣的:(EventBus最全源码解析)
嵌入式linux bootloader,嵌入式系统启动之bootloader 源码解析
三月十六
嵌入式linux bootloader
要探讨bootloader,我们首先从全局来看看,嵌入式系统启动流程是怎么样的。大体上一个嵌入式Linux系统从软件角度分析可以分为四个部分:引导加载程序(bootloader),Linux内核,文件系统,应用程序。当系统首次引导时,或系统被重置时,bootloader首先被执行(位于Flash/ROM中的已知位置处)的代码。它主要用来初始化处理器及外设,然后调用Linux内核。Linux内核在完
Python从0到100(十八):面向对象编程应用
是Dream呀
python 开发语言
前言:零基础学Python:Python从0到100最新最全教程。想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!欢迎大家订阅专栏:零基础学Python:Python从0到100最新
聊聊Netty那些事儿之Reactor在Netty中的实现(创建篇)
Java小海.
java 开发语言 后端 程序人生 spring boot
本系列Netty源码解析文章基于4.1.56.Final版本在上篇文章《聊聊Netty那些事儿之从内核角度看IO模型》中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以及ReactorIO线程模型的底层基石IO多路复用技术在内核中的实现原理。最后我们引出了netty中使用的主从ReactorIO线程模型。通过上篇文章的介绍,我们已经清楚了在IO调用的过程中内核帮我们搞了哪些事情,那
【大数据平台】大数据平台的云迁移策略
野老杂谈
大数据平台建设指南 大数据 大数据平台 云计算 云迁移 数据同步
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏:⭐️全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题.⭐️AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。⭐️大数据平台建设指南:全面讲解从数据采集到数据可视化的整个过程,掌握构建现代化数据平台的核心技术和方法。⭐️《遇见Python:初识、了解与热恋》:涵盖了Pytho
VsCode使用
keep one's resolveY
前端 vscode ide 编辑器
vscode前端vue项目启动:Vue项目的创建启动及注意事项-CSDN博客vscode使用教程:史上最全vscode配置使用教程-夏天的思考-博客园vscode如何从git拉取代码:vscode如何从git拉取代码•Worktile社区
2万字长文,九篇论文读懂大语言模型的前世今生
人工智能
2万字长文,九篇论文读懂大语言模型的前世今生友情提示:这是一篇2W字长文,但我保证,它绝对值得一读!如果感兴趣的话,感谢关注,点赞转发在看收藏,五键四连,谢谢~更多LLM架构文章:LLM架构专栏近日热文:1.全网最全的神经网络数学原理(代码和公式)直观解释2.大模型进化史:从Transformer到DeepSeek-R1的AI变革之路3.2W8000字深度剖析25种RAG变体:全网最全~没有之一4
Java实战 | 手把手教你编写双色球模拟系统中奖逻辑(附源码解析)
北岸避凶
java 开发语言
一、引言双色球作为国内经典彩票玩法,其核心逻辑是号码匹配与中奖规则判断。本文将用Java语言实现一个简易双色球模拟系统,涵盖用户投注、随机开奖、中奖匹配等功能,并深入解析代码实现中的关键点。文末提供完整源码,适合Java初学者练手学习!二、功能概述用户投注输入6个红球(1-33)和1个蓝球(1-16),号码不可重复。随机开奖系统生成6个红球+1个蓝球作为中奖号码。中奖判断根据红球和蓝球的匹配数量,
C#开发串口通讯软件如何如何避免串口通讯中的资源竞争?
openwin_top
c#串口应用开发问题系列 c# 开发语言 串口 上位机 通讯
microPythonPython最小内核源码解析NI-motion运动控制c语言示例代码解析python编程示例系列python编程示例系列二python的Web神器Streamlit如何应聘高薪职位在C#中开发串口通讯软件时,避免资源竞争是确保系统稳定性和数据完整性的关键。资源竞争通常发生在多个线程或进程同时访问同一个串口资源时。为了避免这种情况,可以采取以下措施:使用锁机制(Lock):使用
2025华为OD机试真题目录【E卷+A卷+B卷+C卷+D卷】持续收录中...
耀耀zz
2025最新华为OD机试题目 华为od 算法
摘要本专栏提供2025最新最全的华为OD机试真题库(E+A+B+C+D卷),包括100分和200分题型。题目包含题目描述、输入描述、用例、备注和解题思路、多种语言解法(Java/JS/Py/C/C++)。希望小伙伴们认真学习、顺利通过。声明本专栏的文章主要包括两部分:第一部分:题目描述、输入描述、用例。第二部分:解题思路、源码。第一部分信息来自互联网,不是本专栏付费内容,如果这一部分内容侵犯了原著
【Unity试题】最全的Unity面试题
coder_yz(ง •_•)ง
unity3d Unity 面试题 3d 面试
这个是我刚刚整理出的Unity面试题,为了帮助大家面试,同时帮助大家更好地复习Unity知识点,如果大家发现有什么错误,(包括错别字和知识点),或者发现哪里描述的不清晰,请在下面留言,我会重新更新,希望大家共同来帮助开发者一:什么是协同程序?在主线程运行的同时开启另一段逻辑处理,来协助当前程序的执行,协程很像多线程,但是不是多线程,Unity的协程实在每帧结束之后去检测yield的条件是否满足。二
《Python趣味编程》专栏介绍与专栏目录
Want595
Python趣味编程 python 开发语言
专栏介绍欢迎订阅《Python趣味编程》专栏!全网最全、最新、最详细的原创趣味案例!全网阅读量1000w+,代码分享量10w+含跳动的爱心、无限弹窗、满屏飘字、流星雨、新春烟花等代码新增Python小游戏,含球球大作战、开心消消乐、愤怒的小鸟等代码所有代码在Windows、MacOS、Linux等操作系统都可以运行。本专栏目前含180+趣味案例,目前还在持续更新和涨价中……订阅后可查看专栏内的所有
Mac 快捷键最全版:提升效率的必备技能(特别版)
C_V_Better
mac macos
目录一、与Windows的差异二、常用快捷键1.系统操作2.截图3.文件管理器4.浏览器5.文本处理三、总结在使用Mac电脑时,掌握快捷键可以大大提高工作效率。本文将详细介绍Mac系统中最常用、最实用的快捷键,帮助你更好地利用Mac电脑。一、与Windows的差异在开始之前,我们先来认识一下Mac键盘上几个独特的按键,如⌘(Command/Cmd)、⌥(Option)、⌃(Control)、⇧(S
2024年Android最全Android组件内核之Activity调用栈分析(一)(1),Android面试题集锦在这里
2301_82243558
程序员 android 面试 学习
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!起源Activity是用户交互的第一接口,他提供了
Spring 源码硬核解析系列专题(扩展篇):Spring Batch 的恢复机制源码解析
yinlongfei_love
spring batch java
在第九期中,我们深入探讨了SpringBatch的批处理流程,剖析了Job和Step的执行机制。在企业级应用中,批处理任务可能因异常(如数据库故障、网络中断)失败,如何从失败点恢复并继续执行,是SpringBatch的关键特性之一。本篇将聚焦SpringBatch的恢复机制,深入源码分析其实现原理,并补充相关图示。1.恢复机制的核心概念SpringBatch的恢复机制依赖以下组件:JobRepos
2024年linux——环境基础开发工具使用_gcc test file(4),2024年最新程序设计+Linux运维+Web+数据库+框架+分布式
2401_83740189
程序员 运维 linux 前端
最全的Linux教程,Linux从入门到精通======================linux从入门到精通(第2版)Linux系统移植Linux驱动开发入门与实战LINUX系统移植第2版Linux开源网络全栈详解从DPDK到OpenFlow第一份《Linux从入门到精通》466页====================内容简介====本书是获得了很多读者好评的Linux经典畅销书**《Linu
C C++最全Linux下TCP IP编程--TCP实战_linux tcp,2024最新网易C C++面试题目
2401_84973304
程序员 c语言 c++ 学习
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以添加戳这里获取一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!printf("====waitingforcl
宝塔面板申请SSL安全证书一直显示“待域名确认”?如何处理解决?
青云网运维
宝塔面板教程 WordPress教程
现在越来越多的站点加入到https的大军中来了,主要还是有很多免费的SSL证书可以申请,还有很多平台可以帮助我们一键申请域名证书,比如宝塔面板就支持这样的操作(运维大神可以右上角关闭了,我们小白喜欢用面板),如果还有不了解宝塔面板怎么使用的小伙伴,可以看下我总结的系列教程,保证从新手变老鸟:【宝塔面板精选教程汇总】宝塔面板教程(1)基于云服务器搭建宝塔面板教程最全详解宝塔面板教程(2)宝塔面板添加
DeepSeek怎么用,DeepSeek使用指南最全合集(保姆级教程)
xiecoding.cn
deepseek deepseek使用指南 deepseek怎么用 deepseek免费教学 deepseek资料合集
DeepSeek是一款由国内顶尖团队开发的人工智能大模型,旨在为用户提供高效、智能的问答和知识服务。作为国产AI模型的代表,DeepSeek不仅在自然语言处理(NLP)领域表现出色,还在多个应用场景中展现了强大的能力。与ChatGPT等国际知名模型相比,DeepSeek在中文语境下的表现尤为突出,能够更好地理解中文的复杂语义和文化背景。DeepSeek使用资源下载为了方便大家更好地学习和使用Dee
学习笔记08——ConcurrentHashMap实现原理及源码解析
码代码的小仙女
高级开发必备技能 哈希算法 算法
1.概述为什么需要ConcurrentHashMap?解决HashMap线程不安全问题:多线程put可能导致死循环(JDK7)、数据覆盖(JDK8)优化HashTable性能:通过细粒度锁替代全局锁,提高并发度对比表特性HashMapHashTableConcurrentHashMap线程安全否是是锁粒度无锁全局锁分段锁/CAS+synchronized并发性能高极低高Null键/值允许不允许不允
深入理解 Java 中的 ArrayList
^辞安
java 开发语言 idea
1.引言ArrayList是Java集合框架中最常用的数据结构之一。它基于动态数组实现,提供了快速的随机访问和高效的尾部插入操作。无论是初学者还是资深开发者,`ArrayList`都是日常开发中不可或缺的工具。本文将深入探讨`ArrayList`的实现原理、常见操作及其性能特点,并结合源码解析其内部机制。2.ArrayList的基本概念2.1什么是ArrayList?ArrayList是Java集
基于图论的产业网络知识图谱挖掘与构建
罗伯特之技术屋
智能科学与技术专栏 知识图谱 人工智能
摘要我国是全球产业规模最大、产业覆盖最全的国家,但受多种因素的影响,发现产业链的堵点断点、识别卡点、寻找代替通路、全面优化产业链势在必行。从数据底座构建、核心知识图谱挖掘、兼容传统产业链知识3个方面,阐述了基于图论的产业网络知识图谱的构建过程,以实现产业优化升级与模拟仿真。分析了产业网络知识图谱的应用场景和优势,并给出了其在集成电路行业的应用案例。关键词:图论;产业图谱;知识网络0引言产业经济是国
Spring注解驱动开发之@Autowired
从不吃红薯
基于注解管理Bean spring java 后端
一、@Autowired注解概述1.1核心作用@Autowired是Spring框架中实现依赖注入的核心注解,采用类型优先的自动装配策略。其设计初衷是为了简化XML配置,实现更优雅的组件管理。1.2源码解析@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,Eleme
如何通过 CDN 加速网络?全网最全!!!
職場上的造物主
数据库 网络 安全 经验分享 笔记
PythonCDN加速测速示例通过以下Python脚本,我们可以测试不同CDN节点的响应时间,选择最优的节点进行加速:importtimeimportrequests#CDN节点列表cdns=['https://cdn1.example.com','https://cdn2.example.com','https://cdn3.example.com']#测试CDN响应时间deftest_cdn_
全网最全,软件测试-adb常用命令/日志命令(汇总)
测试追风
软件测试 软件测试工程师 App测试 adb 软件测试 自动化测试 功能测试 App测试 App自动化测试 软件测试工程师
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言adb基本语法adb[-d|-e|-s]命令行参数:-d:指定当前唯一通过USB连接的Android设备为命令目标;-e:指定当前唯一运行的模拟器为命令目标;-s:指定相
Python从0到100(六十八):Python OpenCV-图像边缘检测及图像融合
是Dream呀
opencv python 计算机视觉
前言:零基础学Python:Python从0到100最新最全教程。想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!欢迎大家订阅专栏:零基础学Python:Python从0到100最新
监听其他音频播放时暂停正在播放的音频
至_臻
音视频 前端 vue.js
要实现当有其他音频播放时暂停当前音频,你可以使用全局事件总线或Vuex来管理音频播放状态。这里我将展示如何使用一个简单的事件总线来实现这个功能。首先,你需要创建一个事件总线。你可以在项目的一个公共文件中创建它,例如eventBus.js:importVuefrom'vue';exportconstEventBus=newVue();然后,在你的组件中使用这个事件总线来监听和触发音频播放事件:imp
最全docker-compose部署kafka、SASL模式(密码校验模式)_system
2401_84182428
程序员 docker kafka 容器
Client{org.apache.zookeeper.server.auth.DigestLoginModulerequiredusername="admin"password="123456";};Server{org.apache.zookeeper.server.auth.DigestLoginModulerequiredusername="admin"password="123456"u
2024年最全Python入门的60个基础练习(二)(1)
2401_84281588
程序员 python 开发语言
data=f.read(4)#读4字节f.readline()#读到换行符、n结束f.readlines()#把每一行数据读出来放到列表中f.close()################################f=open(‘/tmp/passwd’)forlineinf:print(line,end=‘’)f.close()##############################f
Android LiveData(一):介绍和简单使用
且听风吟9527
框架原理 LiveData 框架原理 源码分析
传统的组件间的通信方式有Handler、BroadcastReceiver,Interface、EventBus等等方式实现,他们有自己适合的应用场景,也有各自的弊端。这里介绍新的组件通信同时LiveData,它是一个数据持有类,具有以下特点:数据可以被观察者订阅能够感知组件(Fragment、Activity、Service))的生命周期组件处于active状态时,会通知观察者有数据更新对于观察
最全中文对话数据集(不定期更新)
数据猎手小k
人工智能 大数据
随着人工智能技术的发展,自然语言处理(NLP)领域中的对话系统逐渐成为研究的热点。为了提升对话系统的性能,需要大量的高质量对话数据来训练和优化模型。然而,中文对话数据相对于英文来说较为稀缺,且质量参差不齐,这限制了中文对话系统的发展。因此,构建大规模、高质量的中文对话数据集成为了一个迫切的需求。一、研究意义1、推动中文NLP发展:大规模高质量的中文对话数据集能够为中文自然语言处理领域的研究提供基础
mysql主从数据同步
林鹤霄
mysql主从数据同步
配置mysql5.5主从服务器(转)
教程开始:一、安装MySQL
说明:在两台MySQL服务器192.168.21.169和192.168.21.168上分别进行如下操作,安装MySQL 5.5.22
二、配置MySQL主服务器(192.168.21.169)mysql -uroot -p &nb
oracle学习笔记
caoyong
oracle
1、ORACLE的安装
a>、ORACLE的版本
8i,9i : i是internet
10g,11g : grid (网格)
12c : cloud (云计算)
b>、10g不支持win7
&
数据库,SQL零基础入门
天子之骄
sql 数据库入门 基本术语
数据库,SQL零基础入门
做网站肯定离不开数据库,本人之前没怎么具体接触SQL,这几天起早贪黑得各种入门,恶补脑洞。一些具体的知识点,可以让小白不再迷茫的术语,拿来与大家分享。
数据库,永久数据的一个或多个大型结构化集合,通常与更新和查询数据的软件相关
pom.xml
一炮送你回车库
pom.xml
1、一级元素dependencies是可以被子项目继承的
2、一级元素dependencyManagement是定义该项目群里jar包版本号的,通常和一级元素properties一起使用,既然有继承,也肯定有一级元素modules来定义子元素
3、父项目里的一级元素<modules>
<module>lcas-admin-war</module>
<
sql查地区省市县
3213213333332132
sql mysql
-- db_yhm_city
SELECT * FROM db_yhm_city WHERE class_parent_id = 1 -- 海南 class_id = 9 港、奥、台 class_id = 33、34、35
SELECT * FROM db_yhm_city WHERE class_parent_id =169
SELECT d1.cla
关于监听器那些让人头疼的事
宝剑锋梅花香
画图板 监听器 鼠标监听器
本人初学JAVA,对于界面开发我只能说有点蛋疼,用JAVA来做界面的话确实需要一定的耐心(不使用插件,就算使用插件的话也没好多少)既然Java提供了界面开发,老师又要求做,只能硬着头皮上啦。但是监听器还真是个难懂的地方,我是上了几次课才略微搞懂了些。
JAVA的遍历MAP
darkranger
map
Java Map遍历方式的选择
1. 阐述
对于Java中Map的遍历方式,很多文章都推荐使用entrySet,认为其比keySet的效率高很多。理由是:entrySet方法一次拿到所有key和value的集合;而keySet拿到的只是key的集合,针对每个key,都要去Map中额外查找一次value,从而降低了总体效率。那么实际情况如何呢?
为了解遍历性能的真实差距,包括在遍历ke
POJ 2312 Battle City 优先多列+bfs
aijuans
搜索
来源:http://poj.org/problem?id=2312
题意:题目背景就是小时候玩的坦克大战,求从起点到终点最少需要多少步。已知S和R是不能走得,E是空的,可以走,B是砖,只有打掉后才可以通过。
思路:很容易看出来这是一道广搜的题目,但是因为走E和走B所需要的时间不一样,因此不能用普通的队列存点。因为对于走B来说,要先打掉砖才能通过,所以我们可以理解为走B需要两步,而走E是指需要1
Hibernate与Jpa的关系,终于弄懂
avords
java Hibernate 数据库 jpa
我知道Jpa是一种规范,而Hibernate是它的一种实现。除了Hibernate,还有EclipseLink(曾经的toplink),OpenJPA等可供选择,所以使用Jpa的一个好处是,可以更换实现而不必改动太多代码。
在play中定义Model时,使用的是jpa的annotations,比如javax.persistence.Entity, Table, Column, OneToMany
酸爽的console.log
bee1314
console
在前端的开发中,console.log那是开发必备啊,简直直观。通过写小函数,组合大功能。更容易测试。但是在打版本时,就要删除console.log,打完版本进入开发状态又要添加,真不够爽。重复劳动太多。所以可以做些简单地封装,方便开发和上线。
/**
* log.js hufeng
* The safe wrapper for `console.xxx` functions
*
哈佛教授:穷人和过于忙碌的人有一个共同思维特质
bijian1013
时间管理 励志人生 穷人 过于忙碌
一个跨学科团队今年完成了一项对资源稀缺状况下人的思维方式的研究,结论是:穷人和过于忙碌的人有一个共同思维特质,即注意力被稀缺资源过分占据,引起认知和判断力的全面下降。这项研究是心理学、行为经济学和政策研究学者协作的典范。
这个研究源于穆来纳森对自己拖延症的憎恨。他7岁从印度移民美国,很快就如鱼得水,哈佛毕业
other operate
征客丶
OS osx
一、Mac Finder 设置排序方式,预览栏 在显示-》查看显示选项中
二、有时预览显示时,卡死在那,有可能是一些临时文件夹被删除了,如:/private/tmp[有待验证]
--------------------------------------------------------------------
若有其他凝问或文中有错误,请及时向我指出,
我好及时改正,同时也让我们一
【Scala五】分析Spark源代码总结的Scala语法三
bit1129
scala
1. If语句作为表达式
val properties = if (jobIdToActiveJob.contains(jobId)) {
jobIdToActiveJob(stage.jobId).properties
} else {
// this stage will be assigned to "default" po
ZooKeeper 入门
BlueSkator
中间件 zk
ZooKeeper是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题。网上对ZK的应用场景也有不少介绍,本文将结合作者身边的项目例子,系统地对ZK的应用场景进行一个分门归类的介绍。
值得注意的是,ZK并非天生就是为这些应用场景设计的,都是后来众多开发者根据其框架的特性,利
MySQL取得当前时间的函数是什么 格式化日期的函数是什么
BreakingBad
mysql Date
取得当前时间用 now() 就行。
在数据库中格式化时间 用DATE_FORMA T(date, format) .
根据格式串format 格式化日期或日期和时间值date,返回结果串。
可用DATE_FORMAT( ) 来格式化DATE 或DATETIME 值,以便得到所希望的格式。根据format字符串格式化date值:
%S, %s 两位数字形式的秒( 00,01,
读《研磨设计模式》-代码笔记-组合模式
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.util.ArrayList;
import java.util.List;
abstract class Component {
public abstract void printStruct(Str
4_JAVA+Oracle面试题(有答案)
chenke
oracle
基础测试题
卷面上不能出现任何的涂写文字,所有的答案要求写在答题纸上,考卷不得带走。
选择题
1、 What will happen when you attempt to compile and run the following code? (3)
public class Static {
static {
int x = 5; // 在static内有效
}
st
新一代工作流系统设计目标
comsci
工作 算法 脚本
用户只需要给工作流系统制定若干个需求,流程系统根据需求,并结合事先输入的组织机构和权限结构,调用若干算法,在流程展示版面上面显示出系统自动生成的流程图,然后由用户根据实际情况对该流程图进行微调,直到满意为止,流程在运行过程中,系统和用户可以根据情况对流程进行实时的调整,包括拓扑结构的调整,权限的调整,内置脚本的调整。。。。。
在这个设计中,最难的地方是系统根据什么来生成流
oracle 行链接与行迁移
daizj
oracle 行迁移
表里的一行对于一个数据块太大的情况有二种(一行在一个数据块里放不下)
第一种情况:
INSERT的时候,INSERT时候行的大小就超一个块的大小。Oracle把这行的数据存储在一连串的数据块里(Oracle Stores the data for the row in a chain of data blocks),这种情况称为行链接(Row Chain),一般不可避免(除非使用更大的数据
[JShop]开源电子商务系统jshop的系统缓存实现
dinguangx
jshop 电子商务
前言
jeeshop中通过SystemManager管理了大量的缓存数据,来提升系统的性能,但这些缓存数据全部都是存放于内存中的,无法满足特定场景的数据更新(如集群环境)。JShop对jeeshop的缓存机制进行了扩展,提供CacheProvider来辅助SystemManager管理这些缓存数据,通过CacheProvider,可以把缓存存放在内存,ehcache,redis,memcache
初三全学年难记忆单词
dcj3sjt126com
english word
several 儿子;若干
shelf 架子
knowledge 知识;学问
librarian 图书管理员
abroad 到国外,在国外
surf 冲浪
wave 浪;波浪
twice 两次;两倍
describe 描写;叙述
especially 特别;尤其
attract 吸引
prize 奖品;奖赏
competition 比赛;竞争
event 大事;事件
O
sphinx实践
dcj3sjt126com
sphinx
安装参考地址:http://briansnelson.com/How_to_install_Sphinx_on_Centos_Server
yum install sphinx
如果失败的话使用下面的方式安装
wget http://sphinxsearch.com/files/sphinx-2.2.9-1.rhel6.x86_64.rpm
yum loca
JPA之JPQL(三)
frank1234
orm jpa JPQL
1 什么是JPQL
JPQL是Java Persistence Query Language的简称,可以看成是JPA中的HQL, JPQL支持各种复杂查询。
2 检索单个对象
@Test
public void querySingleObject1() {
Query query = em.createQuery("sele
Remove Duplicates from Sorted Array II
hcx2013
remove
Follow up for "Remove Duplicates":What if duplicates are allowed at most twice?
For example,Given sorted array nums = [1,1,1,2,2,3],
Your function should return length
Spring4新特性——Groovy Bean定义DSL
jinnianshilongnian
spring 4
Spring4新特性——泛型限定式依赖注入
Spring4新特性——核心容器的其他改进
Spring4新特性——Web开发的增强
Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC
Spring4新特性——Groovy Bean定义DSL
Spring4新特性——更好的Java泛型操作API
Spring4新
CentOS安装Mysql5.5
liuxingguome
centos
CentOS下以RPM方式安装MySQL5.5
首先卸载系统自带Mysql:
yum remove mysql mysql-server mysql-libs compat-mysql51
rm -rf /var/lib/mysql
rm /etc/my.cnf
查看是否还有mysql软件:
rpm -qa|grep mysql
去http://dev.mysql.c
第14章 工具函数(下)
onestopweb
函数
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
POJ 1050
SaraWon
二维数组 子矩阵 最大和
POJ ACM第1050题的详细描述,请参照
http://acm.pku.edu.cn/JudgeOnline/problem?id=1050
题目意思:
给定包含有正负整型的二维数组,找出所有子矩阵的和的最大值。
如二维数组
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
中和最大的子矩阵是
9 2
-4 1
-1 8
且最大和是15
[5]设计模式——单例模式
tsface
java 单例 设计模式 虚拟机
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
安全的单例模式:
/*
* @(#)Singleton.java 2014-8-1
*
* Copyright 2014 XXXX, Inc. All rights reserved.
*/
package com.fiberhome.singleton;
Java8全新打造,英语学习supertool
yangshangchuan
java superword 闭包 java8 函数式编程
superword是一个Java实现的英文单词分析软件,主要研究英语单词音近形似转化规律、前缀后缀规律、词之间的相似性规律等等。Clean code、Fluent style、Java8 feature: Lambdas, Streams and Functional-style Programming。
升学考试、工作求职、充电提高,都少不了英语的身影,英语对我们来说实在太重要