Concurrent Hash Table Assignment
Contents
Motivation
For this exercise you will create a thread safe data structure. You have previously created a non-thread safe HashTable. Now, with the help of multiple ReentrantReadWriteLocks you will create a thread safe Map that both deals with contention and allows simultaneous gets.
Previous Exercise
You will directly DefaultEntry and HashUtils from the Hashtable exercise.
Further, you will be implementing thread-safe versions of put, get, computeIfAbsent, and compute methods (along with many of the utility methods) from HashTable.
Java Utilities To Investigate
interface Lock
interface ReadWriteLock
class ReentrantReadWriteLock implements ReadWriteLock
Code To Implement
ConcurrentHashTable
None of the methods should be protected by synchronized. You will use the correct ReadWriteLock in the correct mode (Read or Write) to keep your ConcurrentHashTable thread-safe.
constructor
method: public ConcurrentHashTable(int chainCount, ToIntFunction<K> hashFunction)
(sequential implementation only)
Much like the constructor from the not-thread safe HashTable, with the added responsibility of creating the ReadWriteLocks.
For this assignment you will create one instance of ReadWriteLock for each chain. Each ReadWriteLock will be responsible for the chain with the same array index.
Life After 231 Note:
In this assignment we will simply allocate a ReadWriteLock for each chain. Each lock will be responsible for the chain with the same array index.
However, it is reasonable in some circumstances to have more than one chain protected by a single lock. For example, if you had 1024 chains and only wanted to allocate 16 Locks, each Lock would be responsible for 1024/16 (64) chains. The reduced cost in allocating Locks might be worth if you were willing to accept that tasks on separate chains would block if they were protected by the same lock.
Again, for this assignment, we will simply allocate a ReadWriteLock for each chain.
support methods
Many of these methods are equivalent to their counterparts in the HashTable assignment. One exception is that chainOf(key) is replaced by chainIndexOf(key) due to the fact that the chain index is used for both the chain and also the ReadWriteLock protecting it..
These support methods must remain unprotected by any Lock (either intrinsic via synchronized or explicit via a Lock). Ensuring thread-safety will be the responsibility of the public methods: put, get, computeIfAbsent, and compute.
hashFunction()
akin to the HashTable version of hashFunction().
chains()
akin to the HashTable version of chains().
readWriteLocks()
new to this exercise
chainIndexOf(key)
One of the methods you built on HashUtils should be useful here.
Note: In the prior NotThreadSafeHashTable exercise, we only needed the chain for a particular key so directly asking for it via one of the HashUtils methods made sense. Here we will need the index of the chain for both the chain and for determining the lock responsible for protecting that chain, so we ask for the index of the chain in its array.
lockIndexOfChainIndex(chainIndex)
As previously stated we will be creating a ReadWriteLock for each chain, so lockIndexOfChainIndex(chainIndex) simply returns the chainIndex.
provided:
private int lockIndexOfChainIndex(int chainIndex) {
return chainIndex;
}
entryOf(chain, key)
akin to the HashTable version of entryOf(chain, key).
public methods
Be sure to make each of these methods thread safe by acquiring the appropriate (read or write) lock for the appropriate bucket.
Which method, lock() or tryLock(), is the appropriate one to use for creating a thread safe data structure such as this?
Whenever you acquire a lock, but sure to leverage try finally and unlock() the lock within the finally block.
Warning: Do NOT call get() from put, computeIfAbsent, or compute. It will result in a nonintuitive error. entryOf(chain, key) gives you everything you need. |
Warning: Do NOT call any of your public methods from eachother. Simply use your unprotected utility methods. |
Tip: Be sure to make your ConcurrentBucketHashMap thread-safe. If any code is going to access shared mutable data, it needs to do so in a thread-safe manner. |
put(key, value)
method: public V put(K key, V value)
(thread-safe required)
akin to the HashTable version of put(key, value) with the critical add requirement of thread-safety.
get(key)
method: public V get(K key)
(thread-safe required)
akin to the HashTable version of get(key) with the critical add requirement of thread-safety.
computeIfAbsent(key, mappingFunction)
method: public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
(thread-safe required)
akin to the HashTable version of computeIfAbsent(key, mappingFunction) with the critical add requirement of thread-safety.
compute(key, remappingFunction)
method: public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
(thread-safe required)
akin to the HashTable version of compute(key, remappingFunction) with the critical add requirement of thread-safety.
Testing Your Solution
class: | __ConcurrentHashTableTestSuite.java | |
package: | hashtable.concurrent.exercise | |
source folder: | testing/src/test/java |
Pledge, Acknowledgments, Citations
file: | exercise-concurrent-hash-table-pledge-acknowledgments-citations.txt |
More info about the Honor Pledge