【问题标题】:Android runOnUiThread thread safetyAndroid runOnUiThread 线程安全
【发布时间】:2013-08-05 10:47:50
【问题描述】:

我想知道我解决特定问题的方法是否是线程安全的(可能不是,这就是我要问的原因)。假设我们有这段代码,在非 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 代码,那么最好的方法是什么?

谢谢

【问题讨论】:

  • 在初始化 myMap 地图后运行此代码,因此它不会为空。
  • 你是从哪个线程调用的?如果是从 UiThread 调用的,会直接内联执行,就好像没有Runnable一样。
  • 我忘了提到代码不是从 UI 线程运行的。将在其中进行编辑。

标签: java android concurrency


【解决方案1】:

这就像在执行您给出的代码之前定义 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
}

【讨论】:

  • myMap 实际上是对我从方法调用中获取的地图的引用。但这没关系 - 在我通过该方法获得它之前,我现在按照您的建议构造一个初始容量为 0 的新 HashMap。我希望它能起作用,因为没有简单的方法可以重现这个问题......
  • myMap 应该是最终的以避免stackoverflow.com/questions/16062274/…
【解决方案2】:

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

启动整个进程的线程检查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 两次并且每次都可能不同。例如。在if 中它不是空的,而是null 一旦你访问它。一种解决方案是复制参考,这样在您使用该参考时,您的本地副本不会发生任何变化。

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 来解决。

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

【讨论】:

  • 感谢这篇博文,让我深思。在进一步检查了我正在处理的代码(不是我自己的代码,而是遗留代码)之后,结果发现导致我的问题的全局地图引用是多余的,所以我完全删除了它。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 2010-10-28
    • 1970-01-01
    相关资源
    最近更新 更多