Reputation: 225
I am trying to understand ConcurrentHashMap
and see if I can leverage it w/o adding any locks on my side. I have a ConcurrentHashMap
with number of books at the beginning of a day.
class Z {
val books: ConcurrentHashMap<String, Int> = ConcurrentHashMap()
fun addBook(book: String, numberOfBooks: Int) {
books[book] = numberOfBooks
}
fun doSomething(book: String) {
val numberOfBooks = books.remove(book)
}
}
The above would be threadsafe. But now if I need to add in a validation just to be sure the book isn't being added twice during initialization I need to add a synchronized
block just to be sure that I am not adding something like below.
class Z {
val books: ConcurrentHashMap<String, Int> = ConcurrentHashMap()
val lock = Any()
fun addBook(book: String, numberOfBooks: Int) {
synchronized(lock) {
val existingBook = books[book]
if(existingBook!=null)
println("Book exists, BEWARE!!")
books[book] = numberOfBooks
}
}
fun doSomething(book: String) {
var numberOfBooks : Int?
synchronized(lock)
numberOfBooks=books.remove(book)
}
}
Is there a better way for me to do this. I hate adding a synchronized
block just to put in a log statement in there.
Upvotes: 8
Views: 30871
Reputation: 18627
You're in luck: the Map interface has several methods which can help to do this sort of thing atomically, and so are thread-safe in ConcurrentHashMap.
In this case, you don't even need anything exotic, just an explicit call to the put() method, as it returns the previous value (or null
if there wasn't one).
So you could simply tweak your first example to have:
fun addBook(book: String, numberOfBooks: Int) {
val existingNumber = books.put(book, numberOfBooks)
if (existingNumber != null)
println("Book exists, BEWARE!!")
}
…which does what you want without any synchronisation. (You lose Kotlin's array-access syntactic sugar, but that's clearly outweighed here.)
Other methods which can do complex things atomically are compute(), merge(), putIfAbsent(), replace(), and many more; it's worth taking a look through the class so you can recognise situations where they'd come in useful. As you clearly know, synchronization can bring a hefty performance penalty, so if you can avoid it without sacrificing safety, it can be a big win.
Upvotes: 11