双重加锁检查和单例
1. 近日,在做项目中实现集合的异步回调功能,使用了双重加锁检查的机制解决同步问题,原先代码如下:
public void obtainNewestInterface(Context context, UpdateBean config, ObtainListener obtainListener) {
if (mUpdateObject == null) {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
这个函数用于获取一个功能的最新对象,如果此对象已经加载完毕(行3),直接回调(行15)。
否则,将回调加入集合中(行5),如果队列没有在运行加载任务(行9),就可以加载启动任务,加载任务的部分代码如下:
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
mObtainListeners.clear();
}
}
}
加载最新功能对象完成后,将遍历需要加载的集合(行9),一一回调(行11),然后清空(行13)。
这个是没有逻辑问题,但是在多线性同时调用obtainNewestInterface方法的时候,会产生同步的问题,由于没有加入同步锁,会导致集合的对象出现以下错误:
1. 线程A运行至加载完成,遍历回调的时候,线程B运行至mObtainListeners.add(obtainListener);,会出现同步错误。
2. 线程A运行至加载完成,遍历完成的时候,线程B运行至判断是否需要提交任务,这时候size=2,线程A遍历完成,线程B不再提交任务,那么B任务就不会回调
于是加入了锁安全,代码如下:
public void obtainNewestInterface(Context context, UpdateBean config,
ObtainListener obtainListener) {
if (mUpdateObject == null) {
synchronized (mObtainListeners) {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
synchronized (mObtainListeners) {
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
}
mObtainListeners.clear();
}
}
}
这样就解决了如上问题,可是仔细品味一番,新的问题又来了:
1. 线程A执行在遍历,线程B被锁至 if (mUpdateObject == null) {
2.线程B执行遍历完毕,清空集合,此时object不为空,锁解除,线程B进入锁代码,会继续add,此时size=1,会继续提交任务
本质是为了判断object为空就加载一次,回调全部,所以就又加上在锁里面判空,代码如下:
public void obtainNewestInterface(Context context, UpdateBean config,
ObtainListener obtainListener) {
if (mUpdateObject == null) {
synchronized (mObtainListeners) {
//代码加入判空
if (mUpdateObject != null) {
obtainListener.onSucceed(mUpdateObject);
} else {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
}
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
//以下代码不变
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
synchronized (mObtainListeners) {
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
}
mObtainListeners.clear();
}
}
}
搞定!