0

我想知道我解决特定问题的方法是否是线程安全的(可能不是,这就是我要问的原因)。假设我们有这段代码,在非 UI 线程上运行:

if (myMap != null)
{
    runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
           Something something = myMap.get(someKey);
           // update some views and stuff                                       
        }
    });
}

我猜我不能保证 myMap 在 runnable 实际执行时有效,对吧?如果是这种情况,我应该将 myMap != null 移动到 run() 方法中还是只是在那里复制它?如果我想确保只有在地图不为空时才执行 runOnUiThread 代码,那么最好的方法是什么?

谢谢

4

3 回答 3

2

这就像在执行您给出的代码之前定义 myMap 一样简单:

    Map<String, String> myMap = new Hashmap<String, String>();
    //if (myMap != null)
   // {
        runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
               Something something = myMap.get(someKey); //myMap won't be null
               // update some views and stuff                                       
            }
        });
   // }

如果您尝试检查内部是否有任何数据,那么您可以使用size()方法

if(myMap.size() != 0) {
//do your stuffs
}
于 2013-08-05T10:50:59.820 回答
1

您的一般结构不是线程安全的

启动整个进程的线程检查myMap并在可能的另一个线程中启动执行。

if (myMap != null) {
    doSomethingInAnotherThread();
}
// something can set `myMap` to null here...

计划运行的代码将在某个时候执行

void inAnotherThread() {
    myMap.access();
}

但没有更多的保证myMap与以前一样。如果有线程可以改变myMap所指的内容,那么做

void inAnotherThread() {
    if (myMap != null) {
        myMap.acess();
    }
}

仍然不是线程安全的,因为您访问myMap了两次,并且每次都可能不同。例如,它在内部不为空,ifnull一旦您访问它。一种解决方案是复制参考,这样当您使用它时,没有任何东西可以更改该参考的本地副本。

void inAnotherThread() {
    Map localReference = myMap;
    if (localReference != null) {
        localReference.acess();
    }
}

这是否是线程安全的取决于myMap. 对于一个是否是volatile(或final)。如果是:保证其他线程可以看到所指内容的最新版本myMap,如果不是:不保证。(注意:我认为runOnUiThread建立了一个happens-before关系,因为它在内部同步,因此您应该有某种保证可以看到参考的最新版本)

一旦你引用了正确的Map实例,接下来的一点就是你可以安全地使用它。一个简单HashMap的不是线程安全的使用。如果您调用.get()它,如果 - 同时 - 另一个线程调用put/ remove/.. 并因此在.get访问它时更改数据,它仍然可能会炸毁您。

您可以将它包装起来,Collections.synchronizedMap(map)这样可以使单个操作像get原子一样,这样其他线程就不会干扰。但它仍然不是所有东西的线程安全。例如,如果您不进行外部同步,则对值进行迭代仍然会失败。这个问题也可以通过使用ConcurrentHashMap支持迭代的 a 来解决。

线程安全取决于很多因素以及您对什么是线程安全的定义。如果您可以保证myMap一旦设置为永远不会更改!= null并且您知道后台线程已完成修改myMap,那么您编写的内容已经是线程安全的,因此 UiThread 可以安全地访问它。

于 2013-08-05T11:38:41.813 回答
1

当您创建一个 Runnable 并将其传递给函数“runOnUiThread()”时,如果当前线程是主线程,它将立即执行或将被放入队列并稍后执行,此时对象可能无效。如果您在工作线程中检查它的有效性会更好(在 Runnable 的 run() 方法中)。

于 2013-08-05T11:04:36.033 回答