2013-08-05 39 views
1

我想知道如果我对特定问题的方法是线程安全的(可能不是,这就是为什么我问)。假设我们有这样的代码,在非UI线程中运行:Android runOnUiThread线程安全

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

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

感谢

+0

在初始化myMap Map之后运行这段代码,所以它不会为空。 –

+0

你打电话给谁?如果它是从UiThread中调用的,它将直接内联执行,就好像没有'Runnable'一样。 – zapl

+0

我忘了提及代码没有从UI线程运行。要编辑它。 – npace

回答

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两次仍不能线程安全的,每次也可以是不同的。例如。一旦你访问它,它在if但是null之内不是null。一种解决方案是复制引用,以便在使用引用时不会更改该引用的本地副本。

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

是否是线程安全取决于myMap。一个是否是volatile(或final)或不是。如果是:其他线程保证可以看到myMap引用的最新版本,如果没有的话:没有保证。 (注:我觉得runOnUiThread建立了之前发生关系,因为它在内部进行同步,因此,你应该有某种形式的保证,看看最近的参考版本)

下一个点,一旦你有给一个参考正确的Map实例就是你可以安全地使用它。简单的HashMap不是线程安全使用。如果你打电话.get()它仍然可以炸毁你如果 - 在同一时间 - 另一个线程调用put/remove/..也因此变化数据,同时.get访问它。

你可以将它包装在Collections.synchronizedMap(map)这将使单个操作像get原子,以便其他线程不能干涉。但它仍然不是线程安全的。例如。如果不进行外部同步,则遍历这些值仍然会失败。这个问题可以通过使用支持迭代的ConcurrentHashMap来解决。

Threadsafety取决于很多因素,并在你的什么的线程安全是定义。什么是你写的已经线程,如果你能保证,一旦它被设置为!= nullmyMap是永远不会改变,你知道后台线程完成修改myMap因此它是安全的UiThread访问它。

+0

感谢这篇广泛的文章,它给了我一些思考。在对我正在处理的代码(不是我自己的代码,但是遗留下来)进行进一步检查后,事实证明导致我的问题的全局地图引用是多余的,因此我将其完全删除。 – npace

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          
      } 
     }); 
    // } 

如果你想检查是否有任何数据在里面那么在哪里都使用尺寸( )方法

​​
+0

myMap实际上是对我从方法调用中获得的映射的引用。但是这并不重要 - 在我通过这个方法得到它之前,我现在根据你的建议构建一个新的初始容量为0的HashMap。我希望它可以工作,因为没有简单的方法来重新创建这个问题... – npace

+0

'myMap'应该是最终避免http://stackoverflow.com/questions/16062274/how-can-an-immutable-object-with-非最终字段是线程不安全 – zapl

1

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