2013-08-05 4 views
1

Мне было интересно, если мой подход к конкретной проблеме является потокобезопасным (возможно, нет, вот почему я спрашиваю). Предположим, у нас есть этот код, работающий на потоке без пользовательского интерфейса:Android runOnUiThread thread security

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

Я предполагаю, что у меня нет никакой гарантии, что MyMap будет действовать к тому времени, исполняемым на самом деле выполняет, не так ли? Если это так, должен ли я перемещать myMap! = Null внутри метода run() или просто дублировать его там? Какой был бы лучший подход, если я хочу убедиться, что код runOnUiThread выполняется, только если карта не равна нулю?

Благодаря

+0

запустите этот код после инициализации myMap Map, поэтому он не будет пустым. –

+0

Из какой темы вы это называете? Если он вызывается из UiThread, он будет выполняться непосредственно в строке, как если бы не было «Runnable». – zapl

+0

Я забыл упомянуть, что код не запускается из потока пользовательского интерфейса. Gonna edit that in. – 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 после его доступа. Одним из решений является копирование ссылки, поэтому ничто не может изменить вашу локальную копию этой ссылки во время работы с ней.

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, который поддерживает итерацию.

Threadsfety зависит от множества факторов и от определения того, что такое поточный сейф. То, что вы написали, уже является потокобезопасным, если вы можете гарантировать, что myMap никогда не будет изменен, если он установлен на != null, и вы знаете, что фоновый поток выполнен с модификацией 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          
      } 
     }); 
    // } 

Если вы пытаетесь проверить, если какие-либо данные внутри, то и может сделать, используя размер () метод

if(myMap.size() != 0) { 
//do your stuffs 
} 
+0

myMap - это фактически ссылка на карту, которую я получаю от вызова метода. Но это не имеет значения - прежде чем я получу его с помощью метода, теперь я создаю новый HashMap начальной емкости 0, согласно вашему совету. Я надеюсь, что это работает, поскольку нет простого способа воссоздать эту проблему ... – npace

+0

'myMap' должен быть окончательным, чтобы избежать http://stackoverflow.com/questions/16062274/how-can-an-immutable-object-with- non-final-fields-be-thread-unsafe – zapl

1

При создании Runnable и передать его в функцию «runOnUiThread()», если текущий поток является основным поток будет выполнен немедленно или будет помещен в очередь и будет выполнен позже, и это время объект может быть недействительным. Было бы лучше проверить его действительность в рабочем потоке (в методе run() Runnable).