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