通过阅读EventBus源码,可以学习的一些知识点

本文深入剖析了EventBus开源库的源码,重点讲解了单例模式、Builder设计模式及ThreadLocal的使用,探讨了其在多线程环境下的高效运行机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

随着技术的发展,各种开源的工具原来越多,在项目开发阶段,我们很多时候都是直接将开源的库拿过来直接使用,慢慢的就变成了轮子的使用者,技术的提升也就变得缓慢了。因此在使用开源库的过程中,我们都有必要去熟悉库的源码,了解作者的设计思想以及用到哪些技术,并将这些技术进行提炼,为自己后期有机会创造轮子打下基础。

EventBus作为我们项目中常用的开源库,在这里提炼了一下这个库中自己觉得有必要了解的知识点。

1,单例

EventBus的实例采用双重检测的单例模式

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

单例的实现方法有很多种,从性能上来讲这种方式是最高的,锁代码块的性能一般比锁方法高几倍,因为锁代码块根据需要,只真对需要同步的地方加锁,因此性能要好很多。这里面其中的一个关键的 volatile ,这个是一定的要加的,关于的volatile的使用,可参考:volatile不会用,怎么办?

2,Builder设计模式

public class EventBusBuilder{
    .....
    .....
    public EventBus build() {
        return new EventBus(this);
    }
}

builder模式的使用目的是为了将构建复杂对象的过程和它的部件解耦。将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

3,ThreadLocal的使用

ThreadLocal在很多的地方都会看见,以前忽略了他的作用,这次结合在EventBus中的使用,把它搞明白。

类说明:

 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 ......
 ......
 Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 

这个注释看了也没有搞明白这个类到底是干啥用的,所以得结合源码加以说明。在EventBus中的实例化如下:

 private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

这个定义比较简单,实例化一个ThreadLocal类,重写initialValue()方法,返回一个默认的PostingThreadState。接下来是对currentPostingThreadState的使用,在post一个event的时候调用:

public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    ......
    ......
}

其中使用到ThreadLocal的get方法()

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

这里面的操作逻辑为获取当前线程,然后获取线程中的ThreadLocalMap对象,代码如下:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

public class Thread implements Runnable {
    ......
    //这是重点,线程中定义一个ThreadLocalMap 对象,使得它只作用于当前线程
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ......
}

在EventBus中,如果多个线程同时调用的post(event)方法时,则是为对应的线程创建PostingThreadState,然后再修改PostingThreadState对象的内容,使得PostingThreadState对象为线程独自使用。

这里ThreadLocalMap可以看做一个简单的<key,value>存储即可,其中key是ThreadLocal对象,value是ThreadLocalMap定义需要保存的泛型值。这个threadLocals定义在Thread内部,显而易见就存储在线程的私有空间,也就不会存在变量在多线程模式下的安全问题。

static class ThreadLocalMap {
   ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
       ......
    }
}

在没有currentPostingThreadState没有调用set(T value)或者首次调用get()方法,getMap(t)返回的是一个空ThreadLocalMap,因此会执行setInitialValue()方法。在EventBus中重写了initialValue()放,因此该地方或得到一个默认的PostingThreadState对象。


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;
}

第一次执行的方法,map都会为空,因此会执行createMap()

void createMap(Thread t, T firstValue) {
	//直接为线程的ThreadLocalMap赋值
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

这里就是简单粗暴的对Thread的threadLocals进行赋值操作。后续的其他操作只要在线程的生命周期类,都可以直接thread.threadLocals获取线程内部保存的变量值。

通过上面的代码,我可以了解Thread,ThreadLocal,ThreadLocalMap直接的关系如下:
在这里插入图片描述

ThreadLocal的作用就是为每个Thread设置一个对象,不共享,只在线程自己的生命周期内有效,这样就不存在并发时共享对象同步问题。那么它和synchronized有没有什么区别呢?区别显而易见,有着本质的差别。synchronized利用锁机制,使变量或者代码块在某一时刻只能被一个线程访问。而ThreadLocal则为每个线程创建一个独立的对象而非公共同一个对象,在线程独立的内存空间,则不存在数据共享。

那么EventBus为什么需要使用ThreadLocal呢?

public void post(Object event) {
    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()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值