I would recommend using a library, because getting concurrency right on your own is hard. You could for example use a concurrent map like TrieMap. See the answer above.
But let's assume that you want to do this manually for educational purposes. The first step to make the above thread-safe would be to use an immutable collection. So instead of
private val myData: Map[String, MyClass2] = new HashMap[String, MyClass2]()
you would use
private var myData = Map.empty[String, MyClass2]
(Even though there is a var here, this has less mutable state than the version above. In this case the only mutable thing is a single reference, whereas in the example above the entire collection is mutable)
Now you have to deal with the var. You have to make sure that an update to the var on one thread is "seen" on all other threads. So you have to mark the field as @volatile. That would be enough if you have a publish/subscribe scenario where writes are only done from one thread. But assuming that you want to both read and write from different threads, you will need to use synchronized for all write accesses.
Clearly, this is enough complexity to warrant introducing a little helper class:
final class SyncronizedRef[T](initial:T) {
@volatile private var current = initial
def get:T = current
def update(f:T=>T) {
this synchronized {
current = f(current)
}
}
}
With this little helper, the code from above can be implemented like this:
class MyClass {
val state = new SyncronizedRef(Map.empty[String, MyClass2])
def someMethod = {
state.update(myData =>
val id = getSomeId
if (myData.containsKey(id))
myData - id
else {
Log.d("123", "Not found!")
myData
}
}
def getSomeId = //....
}
This would be thread-safe as far as the map is concerned. However, whether the whole thing is threadsafe depends on whatever happens in getSomeID.
In general, this way of dealing with concurrency will work as long as the thing passed to update is a pure function that just transforms the data without having any side effects. If your state is more complex than a single map, it can be quite challenging to write your updates in a purely functional style.
There are still low level multithreading primitives in the SynchronizedRef, but the logic of your program is completely free of them. You just describe how the state of your program changes in response to external input by composing pure functions.
In any case, the best solution for this particular example is just to use an existing concurrent map implementation.