Reputation: 1682
I have the following situation:
This is a structure which is heavily modified by multiple threads.
What would be the best data structure for this?
ArrayList. This would be ideal to be able to directly access the last element seen using the index, but it leads to concurrent modifications exceptions. i could make it synchronised, but would like to avoid locking (or any locking apart from the very last element, as it is the only one where there might be concurrent writes to add new elements)
ConcurrentLinkedQueue. This would solve my concurrency problem, but has the problem that I would have to store the current position of the iteration rather than an integer index. This has the problem that it returns a weakly consistent iterator which is not guaranteed to return new objects that have been added to the list since the iterator was created (source: javadoc)
ConcurrentHashMap with index as keys. This has the benefit that I can access data corresponding to the correct index directly, but has the issue that there isn't a "getNext" operator that will allow me to efficiently traverse the elements from index, to index + 1, etc
Vectors This would solve most of my problems in allowing something that won't throw concurrent modification exceptions and allow for direct accessing. However, given all methods are synchronised, the performance is poor compared to arraylists. Given that I only ever want to extend the structure, and not insert records in the middle, I'm reluctant to go for this heavy weight solution, where reads also suffer a performance hit (whereas, given my usecase, the index of an element never actually changes, so there's no need to synchronise reads that are not the tail)
Custom data structure: keep an array of the objects I want to store and a pointer to the tail of this array (the last element set), when inserting a new object, lock the tail and the object pointed to by the tail. When the object exceeds its current size, to a locking resize operation.
What would be the best strategy/ any other more efficient implementation?
Upvotes: 42
Views: 2332
Reputation: 1716
The CopyOnWriteArrayList structure could solve your problem (java.util.concurrent).
CopyOnWriteArrayList
s is thread-safe because all mutative operations are implemented by creating a copy of the list.
The problem of ConcurrentModificationException
is avoided because the array doesn't change while iterated. The so called snapshot style iterator
uses a reference to the state of the array when the iterator was created.
If you have much more reads than writes, use CopyOnWriteArrayList
, otherwise use Vector
.
Vector
introduces a small synchronization delay for each operation, when CopyOnWriteArrayList
has a longer delay for write (due to copying) but no delay for reads.
Vector
requires explicit synchronization when you are iterating it (so write operations can't be executed at the same time), CopyOnWriteArrayList
doesn't.
Upvotes: 11
Reputation: 4552
Do you have to use a single data structure? What if you used two - one for the "active" portion of the list, and one for the "items you have seen" list? You could use a Vector for the "active" portion and some kind of manager that periodically moves items to the "items you have seen" list.
Upvotes: 0
Reputation: 120858
This very much sounds like you would need a distruptor or in simple words lock free queue. I wish I could add an example here, but I only started to work on it yesterday. I could also tell you how it works, or you can read a far better explanation here:
The general idea is that this is completely lock free, it only uses the CAS registers (in java AtomicXXX). I simply fell in love with the idea.
Upvotes: 4
Reputation: 1271
Looking into this I came to the same solution as @MissingNumber.
Use a ConcurrentHashMap as backing data structure:
To add the random access by index use an AtomicInteger to maintain the index and put it as the key to retrieve the map values.
public class ConcurrentListMap {
private final ConcurrentHashMap<Integer, Object> backingMap;
private final AtomicInteger index;
public ConcurrentListMap() {
backingMap = new ConcurrentHashMap();
index = new AtomicInteger(0);
}
public int append(final Object value) {
final int newIndex = index.incrementAndGet();
backingMap.put(newIndex, value);
return newIndex;
}
public Object get(final int entry) {
return backingMap.get(entry);
}
public int getTailIndex() {
return index.get();
}
}
Upvotes: 4
Reputation: 3749
I'm going to offer up ConcurrentSkipListSet
, because:
1) It's concurrent.
2) It's a Set
.
3) It's also NavigableSet
, thus also a SortedSet
.
This gives you a lot of flexibility, much of which you probably won't need. But apart from "You can't add items that already exist" (which I don't know if is a problem, or a boon) it seems to satisfy all your requirements.
Upvotes: 0
Reputation: 1182
ConcurrentHashMap with index as keys could solve your problem , but you need to do litte more work to do so ..
Like following Pseudo Code .
Map<Integer , ConfiguredObject > myMap = new ConcurrentHashMap<Integer,ConfiguredObject >();
class ConfiguredObject
{
YourObject Object;// the object which you want to map for map[key];
ConfiguredObject Next;//the object which you want to map for map[key+1];
ConfiguredObject Previous;//the object which you want to map for map[key-1];
YourObject NextObject;
YourObject PrevObject;
}
So this should solve all your problems.
Concurrency Framework takes care .
Indexing Keys are your indexes .
Iteration , with this code if you have index you can use
myMap.get(key).Next ;
myMap.get(key).Previous ;
All you need to do is define configurable object and write constructor accordingly with care .
Hope this help you.
Upvotes: 1
Reputation: 159
As sk2212 said, I think that java.util.Vector
meets your three points.
add
, which adds elements at the end of the
list.get(index)
to retrieve a concrete element at specific index.Upvotes: 1
Reputation: 1533
ArrayList. This would be ideal to be able to directly access the last element seen using the index, but it leads to concurrent modifications exceptions. i could make it synchronised, but would like to avoid locking (or any locking apart from the very last element, as it is the only one where there might be concurrent writes to add new elements)
You could use a temporary List to put the objects to be added and when the read thing unblock, you add tmpList's content to the ArrayList.
Upvotes: 0