`
heyixian
  • 浏览: 12599 次
社区版块
存档分类
最新评论

双重加锁检查机制的应用

阅读更多
双重加锁检查和单例
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();

            }

        }

 }


搞定!
14
1
分享到:
评论
4 楼 heyixian 2013-04-14  
shenliuyang 写道
CopyOnWriteArrayList 让你happy起来

这里的场景是应用于异步回调的同步性、和安全无关哈。
加锁来解决这个问题是为了
防止业务上的错误,实现异步等待的功能、
3 楼 heyixian 2013-04-14  
xieting0911 写道
博主可以写的再详细些

嗯,下次我会注意、
2 楼 xieting0911 2013-03-26  
博主可以写的再详细些
1 楼 shenliuyang 2013-03-25  
CopyOnWriteArrayList 让你happy起来

相关推荐

Global site tag (gtag.js) - Google Analytics