文末
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。
其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
上面代码获取到了订阅集合,下面就是对集合里面的元素进行订阅
// subscriber:订阅者
//subscriberMethod:订阅方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//Subscription封装了订阅者
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
//分发事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
subscribe方法总结:
1、首先判断是否注册过该事件,如果注册过,就会抛出异常告诉已经注册了
2、然后按照优先级加入到subscriptionsByEventType和value的List中
3、然后添加到typeBySubscriber的value的List中
4、分发事件:checkPostStickyEventToSubscription
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
//最后还是用到了3个线程调度的Poster,3个线程调度的Poster是EventBus的核心
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//POSTING 直接去调用事件
case POSTING:
invokeSubscriber(subscription, event);
break;
// 先判断是否在主线程,在,就会直接调用
//否则通过mainThreadPoster.enqueue将事件发送给主线程执行方法,里面会有一个队列排队的过程,不会阻塞主线程
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//加入队列
mainThreadPoster.enqueue(subscription, event);
}
break;
//mainThreadPoster默认不过null, 所以这种情况下会直接将事件发送给一个队列,然后依次取出,发送给主线程执行,这种情况下不会阻塞主线程
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
//内部通过一个线程池将事件发送给子线程执行,当有新的任务到来的时候,会存放到一个任务队列中,等待任务执行完之后,才会执行新任务
case BACKGROUND:
if (isMainThread) {
//加入队列
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
// 不管是否在主线程都会加入队列中
//内部通过一个线程池将事件发送给子线程执行,当有新的任务到来的时候,直接再开一个新线程
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
> 四、post发送事件
public void post(Object event) {
//发送的事件都是封装到PendingPost中了,然后存到一个消息队列中
PostingThreadState postingState = currentPostingThreadState.get();
//获取事件队列集合,并加入到队列当中
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//是否正在进行发送,如果正在发送,就不发送了
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//通过保存状态的HashMap找到subscriptions的集合,然后依次遍历去调用postToSubscription方法来发送
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
//发送事件的线程状态的封装类
final static class PostingThreadState {
//事件队列集合
final List<Object> eventQueue = new ArrayList<>();
//是否正在发送事件
boolean isPosting;
//是否在主线程
boolean isMainThread;
//订阅者封装起来的封装类
Subscription subscription;
//事件
Object event;
//是否取消
boolean canceled;
}
//postSingleEvent方法调用了postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//通过保存状态的HashMap找到subscriptions的集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//遍历去调用postToSubscription方法来发送
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;
}
//发送消息
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 {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
总结:
EventBus中最核心的就是线程调度,就是3个Poster:HandlerPoster,BackgroundPoster,AsyncPoster,这3个Poster弄懂了,EventBus也就懂了。
HandlerPoster,BackgroundPoster,AsyncPoster三个Poster都是实现了Poster接口,都是通过enqueue发送 事件,发送的事件都是封装到PendingPost中了,然后存到一个消息队列中
* HandlerPoster里面封装的是一个Handler,通过sendMessage将事件从子线程发送给主线程
* BackgroundPoster里面是通过eventBus.getExecutorService().execute执行异步任务,和AsyncPoster不同的是,它执行完异步任务之后,才会去执行下一个任务。
* AsyncPoster里面是通过eventBus.getExecutorService().execute执行异步任务,和BackgroundPoster不同的是,它每次发送一次事件,都会开一个子线程去执行异步任务。
public class HandlerPoster extends Handler implements Poster {}
final class BackgroundPoster implements Runnable, Poster {}
class AsyncPoster implements Runnable, Poster {}
final class PendingPost {
//发送的事件都是封装到PendingPost中了,然后存到一个pendingPostPool 消息队列中
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
}
3个Poster可以结合register和post方法讲解,即通过builder模式创建EventBus的时候,初始化的3个Poster
EventBus.getDefault()的时候就调用了构造方法初始化了3个Poster
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
mainThreadPoster = new HandlerPoster(this)
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
}
//POSTING 直接去调用事件
case POSTING:
//先判断是否在主线程,在,就会直接调用
//否则通过mainThreadPoster.enqueue将事件发送给主线程执行方法,里面会有一个队列排队的过程,不会阻塞主线程
case MAIN:
//mainThreadPoster默认不过null, 所以这种情况下会直接将事件发送给一个队列,然后依次取出,发送给主线程执行,这种情况下不会阻塞主线程
case MAIN_ORDERED:
//内部通过一个线程池将事件发送给子线程执行,当有新的任务到来的时候,会存放到一个任务队列中,等待任务执行完之后,才会执行新任务
case BACKGROUND:
//内部通过一个线程池将事件发送给子线程执行,当有新的任务到来的时候,直接再开一个新线程
case ASYNC:
POSTING:和发送方保持同一线程,发送在主,接收也在主,发送在子,接收也在子
MAIN:不管发送方在哪里,始终在Main线程
ASYNC:不管发送方在哪里,始终开启新线程
BACKGROUND:如果发送方在主线程,BACKGROUND则会新开一个线程;如果发送方在子线程,BACKGROUND则会在相同的子线程中
MAIN\_ORDERED:和MAIN一样,但事件会按先后顺序执行。
1、四种模式分别是:POSTING、MAIN、BACKGROUND、ASYNC。如果是想更新UI就使用MAIN模式,如果要进行耗时操作最好是使用ASYNC,因为这个模式能永远保证在不一样的线程中进行操作,而且都是子线程。
(1)POSTING:这种模式就是eventBus默认的模式,我们在使用的时候不需要再订阅者的方法的注解后面加任何东西(选择模式),但是这种只能在同一个线程中接收,也就是说,如果是在主线程中发布消息就只能在主线程中接收消息,如果是在子线程中,那么也只能在相同的子线程中去接收消息。
### 写在最后
对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。
![](https://img-blog.csdnimg.cn/img_convert/9268d85aa420cd778d04876811843277.webp?x-oss-process=image/format,png)
![![](https://img-blog.csdnimg.cn/img_convert/5521a5abad8ff4eccce978b48f40666a.webp?x-oss-process=image/format,png)
![[]
](https://upload-images.jianshu.io/upload_images/22459598-3e1bbd9b84cc0ef9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](https://img-blog.csdnimg.cn/img_convert/ed19e48608df7223faa946e6bc803c55.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/867c89990d991b8b2fba04c0ca682c57.webp?x-oss-process=image/format,png)
>文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等
>
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
### 写在最后
对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。
[外链图片转存中...(img-c4mDp4Os-1715665771229)]
[外链图片转存中...(img-lam5Fkg1-1715665771229)]
![[]
](https://upload-images.jianshu.io/upload_images/22459598-3e1bbd9b84cc0ef9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
[外链图片转存中...(img-xNAP88KH-1715665771230)]
[外链图片转存中...(img-BDEQFAWU-1715665771230)]
>文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等
>
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**