14

I'm trying to create a concurrent LinkedHashMap for a multithreaded architecture.

If I use Collections#synchronizedMap(), I would have to use synchronized blocks for iteration. This implementation would lead to sequential addition of elements.

If I use ConcurrentSkipListMap is there any way to implement a Comparator to store sequentially, as stored in Linked List or queue.

I would like to use java's built in instead of third party packages.

EDIT:

In this concurrent LinkedHashMap, if the keys are the name, I wish to put the keys in sequence of their arrival. i.e. new value would be appended to either at start or end, but sequentially.

While iterating, the LinkedHashMap could be added with new entries, or removed. but the iteration should be the sequence in which the entries were added.

I understand that by using Collections#synchronizedMap(), an synchronized block for iteration would have to be implemented, but would the map be modifiable (entries could be added/removed) while it is being iterated.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Nilesh
  • 557
  • 2
  • 10
  • 21
  • What getter/setter are you talking about? What are you trying to "store sequentially"? Please give more details - it's hard to understand what you're really trying to do at the moment. – Jon Skeet Apr 23 '10 at 11:56
  • What are you trying to store sequentially? If your keys have a definable order then you should be able to write a Comparator. Perhaps you should provide more information about the keys in your map and how you would like them ordered. – M. Jessup Apr 23 '10 at 11:58

5 Answers5

3

If you use synchronizedMap, you don't have to synchronize externally, except for iteration. If you need to preserve the ordering of the map, you should use a SortedMap. You could use ConcurrentSkipListMap, which is thread-safe, or another SortedMap in combination with synchronizedSortedMap.

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
2

A LinkedHashMap has a doubly linked list running through a hashtable. A FIFO only mutates the links on a write (insertion or removal). This makes implementing a version fairly straightforward.

  1. Write a LHM with only insertion order allowed.
  2. Switch to a ConcurrentHashMap as the hashtable.
  3. Protect #put() / #putIfAbsent() / #remove() with a lock.
  4. Make the "next" field volatile.

On iteration, no lock is needed as you can safely follow the "next" field. Reads can be lock-free by just delegating to the CHM on a #get().

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Ben Manes
  • 9,178
  • 3
  • 35
  • 39
  • No offense, but I hate it when people talk like this. I know what's a doubly linked list. I know what's a hashtable. But what does a double linked list running through a hashtable supposed to mean? – Pacerier Nov 05 '11 at 23:50
  • 1
    Yes, its hard to explain and visualize. See this [presentation](http://concurrentlinkedhashmap.googlecode.com/files/ConcurrentCachingAtGoogle.pdf) where I gave it a try. The idea is that a linked list maintains ordering and requires an O(n) seek. A hashtable is unordered and finds an entry in O(1) time. The combination is that the hash-table entry contains the list pointers so an entry is ordered on the list. Entries in the list can be appended to (FIFO order), moved to the head/tail (LRU order), and removed in O(1) time (eviction). This provides a insertion or access ordered map cheaply. – Ben Manes Nov 06 '11 at 03:02
  • 1
    isn't it true that if I sort my `HashMap`, it would effectively become an "ordered" hashmap, which basically defeats the purpose of a `LinkedHashMap` ? – Pacerier Nov 06 '11 at 05:57
  • 1
    Any comparison based sort is O(lg n) and requires something to compare with (e.g. timestamp). This is not comparison based and is faster for maintaining insertion or access order. – Ben Manes Nov 06 '11 at 06:39
1

Use Collections#synchronizedMap().

As per my belief, if I use Collections.synchronizedMap(), I would have to use synchronized blocks for getter/setter.

This is not true. You only need to synchronize the iteration on any of the views (keyset, values, entryset). Also see the abovelinked API documentation.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • if map is being iterated, then would it be possible for other thread to add or remove elements from the map? – Nilesh Apr 23 '10 at 12:15
  • Would this implementation lead to sequential addition of elements – Nilesh Apr 23 '10 at 12:21
  • 1) Not if you synchronize the iteration as per the documentation (have you clicked the link anyway?). That's the whole point of synchronization. 2) Yes, the addition is already internally synchronized, that's also your whole intent, is it? – BalusC Apr 23 '10 at 12:29
  • 1) No - read the javadoc. 2) Yes - this implied by the javadoc, and guaranteed by the current implementation. – Stephen C Apr 23 '10 at 12:31
  • it is possible to synchronize without serializing all get and put operations. For example, `ConcurrentHashMap` works that way. However, the javadoc implies that operations on a Map returned by this method *will* be serialized. – Stephen C Apr 23 '10 at 12:34
1

Until now, my project used LRUMap from Apache Collections but it is based on SequencedHashMap. Collections proposes ListOrderedMap but none are thread-safe.

I have switched to MapMaker from Google Guava. You can look at CacheBuilder too.

Yves Martin
  • 10,217
  • 2
  • 38
  • 77
0

Um, simple answer would be to use a monotonically increasing key provider that your Comparator operates on. Think AtomicInteger, and every time you insert, you create a new key to be used for comparisons. If you pool your real key, you can make an internal map of OrderedKey<MyRealKeyType>.

class OrderedKey<T> implements Comparable<OrderedKey<T>> {
  T realKey;
  int index;
  OrderedKey(AtomicInteger source, T key) {
    index = source.getAndIncrement();
    realKey = key;
  }
  public int compareTo(OrderedKey<T> other) {
    if (Objects.equals(realKey, other.realKey)) {
      return 0;
    }
    return index - other.index;
  }


}

This would obviate the need for a custom comparator, and give you a nice O(1) method to compute size (unless you allow removes, in which case, count those as well, so you can just subtract "all successful removes" from "all successful adds", where successful means an entry was actually created or removed).

Ajax
  • 2,465
  • 23
  • 20
  • 1
    Do beware memory leaks; you may want to add WeakReference to expensive things... though expensive things tend to be pretty bad as keys in a map, sometimes you just need a dynamic list of tuples (and are too lazy to make a real Object for it), and java lacks the syntax to express that gracefully. Using the actual map semantics, of course, you want a threadsafe insertion ordered map, which OrderedKey enables for you. – Ajax Jan 21 '18 at 11:59