58

Can the following piece of code be rewritten w/o using Collections.synchronizedMap() yet maintaining correctness at concurrency?

Collections.synchronizedMap(new WeakHashMap<Class, Object>());

i.e. is there something from java.util.concurrent one can use instead? Note that merely replacing with

new ConcurrentHashMap<Class, Object>(new WeakHashMap<Class, Object>()));

obviously won't work

hoat4
  • 1,182
  • 12
  • 9
Nikita
  • 6,019
  • 8
  • 45
  • 54
  • 2
    The important benefit of highly concurrent data structures such as `ConcurrentHashMap` is that it can (through a variety of techniques) remain thread-safe under heavy load without (much) blocking. It is important to realize that if your class is not under heavy load, your performance with ConcurrentHashMap could be *worse* than with HashMap. If your environment is expected to be largely free of contention that you can use external synchronization and you'll be just fine. – scottb Jul 08 '15 at 23:42

7 Answers7

43

Guava's CacheBuilder class allows you to do this easily.

CacheBuilder.newBuilder().weakKeys().build()

Note that this changes key equality semantics to be == instead of .equals() which will not matter in your case of using Class instances but is a potential pitfall.

Waldemar Wosiński
  • 1,490
  • 17
  • 28
Steven Schlansker
  • 37,580
  • 14
  • 81
  • 100
  • 1
    Updated link to Guava: https://github.com/google/guava and updated link for CacheBuilder: https://google.github.io/guava/releases/18.0/api/docs/com/google/common/cache/CacheBuilder.html – Brad Cupit Apr 27 '17 at 01:49
23

I don't believe there is. In fact the javadoc suggests using Collections.synchronizedMap()

"Like most collection classes, this class is not synchronized. A synchronized WeakHashMap may be constructed using the Collections.synchronizedMap method."

objects
  • 8,637
  • 4
  • 30
  • 38
  • @rogerdpack: but you could always compose a WeakHashMap into a roll-your-own `ThreadSafeWeakHashMap` and, using a monitor lock, write your own putIfAbsent(...) pretty easily. Agreed that this would be suboptimal under heavy loads but not everything is heavily loaded. – scottb Jul 08 '15 at 23:46
  • 1
    @rogerdpack it does now, Java 8 ftw – qualidafial Sep 30 '16 at 04:55
2

Cafeine is a popular competitor of Guava cache.

- keys automatically wrapped in weak references
- values automatically wrapped in weak or soft references

usage:

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
 .weakKeys()
 .weakValues()
 .build(key -> createExpensiveGraph(key));
Waldemar Wosiński
  • 1,490
  • 17
  • 28
1

Does wrapping the WeakHashMap in a synchronized map still work correctly for what you want to do, since the garbage collector can modify the weakreferences directly at anytime, bypassing the synchronized map wrapper? I think WeakHashMap only truly works in a single threaded model.

As mentioned above, the documentation for WeakHashMap at https://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html specifically says:

"A synchronized WeakHashMap may be constructed using the Collections.synchronizedMap method"

Which implies to me that this technique must work in tandem with the garbage collector's behavior (unless the documentation is buggy!)

  • Correct. The JVM is always multi-threaded. There are GC threads, finalizer threads, and others. If WeakHashMap didn't work in an explicitly multithreaded Java program, it's hard to see how it could work in a single-threaded one either. – Doradus Feb 22 '17 at 13:56
  • Huh? You are missing the whole question. He is asking for a weak version of ConcurrentHashMap (which uses a different algorithm than WeakHashMap). – Stefan Reich Jul 26 '21 at 22:28
1

If you happen to have the Spring Framework in your classpath already, then one option is ConcurrentReferenceHashMap:

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ConcurrentReferenceHashMap.html

You can choose between using weak or soft references (for both the keys and values).

Thomas GL
  • 13
  • 4
-1

If you are using Java 7 and above, this use case is solved in a thread-safe manner with ClassValue https://docs.oracle.com/javase/7/docs/api/java/lang/ClassValue.html If you require the use of remove, think carefully about concurrency and read the doc thoroughly.

If you are using Java 6 or below. No, you have to synchronize a WeakHashMap.

Scott Carey
  • 1,587
  • 17
  • 13
-1

Does wrapping the WeakHashMap in a synchronized map still work correctly for what you want to do, since the garbage collector can modify the weakreferences directly at anytime, bypassing the synchronized map wrapper? I think WeakHashMap only truly works in a single threaded model.

Gene
  • 9
  • 1