How to get java object itself when its about to be collected - java

How can we execute a piece of code using the object (its state is needed) before it gets collected if we don't have control over its source (cant enforce implementing some interface or finally block)?
Java Reference types allow us to access an object if someone else makes it strongly reachable + if we use reference queues we can also be notified when the object is collected, unless my understanding is wrong that's all you can do with reference types, no matter what you use at any point the object is either strongly reachable or its gone and you have null.
All i really need is a way to get notified when specific object is about to be collected.

There is a reason why the Reference API doesn’t allow to retrieve the collected object: allowing to make a collected object reachable again, like happening with the finalize() method, is exactly what is not intended.
The standard approach is to create subclasses of the reference types to store the information associated with the referent, e.g. everything necessary to perform the cleanup action, within the specialized reference object. Of course, this information must not include strong references to the referent itself.
private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
static class IntegerPhantomReference extends PhantomReference<Integer> {
final int value;
public IntegerPhantomReference(Integer ref) {
super(ref, QUEUE);
value = ref.intValue();
}
public String toString() {
return "Integer[value="+value+"]";
}
}
private static final Set<IntegerPhantomReference> REGISTERED = new HashSet<>();
public static void main(String[] args) throws InterruptedException {
List<Integer> stronglyReferenced = new ArrayList<>();
for(int i = 0; i < 10; i++) {
Integer object = new Integer(i);
stronglyReferenced.add(object);
REGISTERED.add(new IntegerPhantomReference(object));
}
gcAndPoll("initial");
stronglyReferenced.removeIf(i -> i%2 == 0);
gcAndPoll("after removing even");
stronglyReferenced.clear();
gcAndPoll("after remove all");
if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
private static void gcAndPoll(String msg) throws InterruptedException {
System.out.println(msg);
System.gc(); Thread.sleep(100);
for(;;) {
Reference<?> r = QUEUE.poll();
if(r == null) break;
System.out.println("collected "+r);
REGISTERED.remove(r);
}
}
initial
after removing even
collected Integer[value=4]
collected Integer[value=8]
collected Integer[value=6]
collected Integer[value=2]
collected Integer[value=0]
after remove all
collected Integer[value=1]
collected Integer[value=5]
collected Integer[value=3]
collected Integer[value=7]
collected Integer[value=9]
all objects collected
For completeness, there is a hack that allows to resurrect a collected object, which will stop working in Java 9.
The documentation of PhantomReference says:
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued.
It’s not clear why this has been specified and the get() method of PhantomReference has been overridden to always return null, exactly to disallow taking any benefit from the fact that this reference has not been cleared. Since the purpose of this special behavior is unclear, it has been removed from the specification in Java 9 and these references are automatically cleared like any other.
But for previous versions, it is possible to use Reflection with access override to access the referent, to do exactly what the API was not intended to allow. Needless to say, that’s just for informational purpose and is strongly discouraged (and as said, it stops working in Java 9).
private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
private static final Set<PhantomReference<Integer>> REGISTERED = new HashSet<>();
public static void main(String[] args)
throws InterruptedException, IllegalAccessException {
List<Integer> stronglyReferenced = new ArrayList<>();
for(int i = 0; i < 10; i++) {
Integer object = new Integer(i);
stronglyReferenced.add(object);
REGISTERED.add(new PhantomReference<>(object, QUEUE));
}
gcAndPoll("initial");
stronglyReferenced.removeIf(i -> i%2 == 0);
gcAndPoll("after removing even");
stronglyReferenced.clear();
gcAndPoll("after remove all");
if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
static final Field REFERENT;
static {
try {
REFERENT = Reference.class.getDeclaredField("referent");
REFERENT.setAccessible(true);
} catch (NoSuchFieldException ex) {
throw new ExceptionInInitializerError(ex);
}
}
private static void gcAndPoll(String msg)
throws InterruptedException, IllegalAccessException {
System.out.println(msg);
System.gc();
Thread.sleep(100);
for(;;) {
Reference<?> r = QUEUE.poll();
if(r == null) break;
Object o = REFERENT.get(r);
System.out.println("collected (and now resurrected)"+o);
REGISTERED.remove(r);
}
}

Related

How to use the same hashmap in multiple threads

I have a Hashmap that is created for each "mailer" class and each "agent" class creates a mailer.
My problem is that each of my "agents" creates a "mailer" that in turn creates a new hashmap.
What I'm trying to do is to create one Hashmap that will be used by all the agents(every agent is a thread).
This is the Agent class:
public class Agent implements Runnable {
private int id;
private int n;
private Mailer mailer;
private static int counter;
private List<Integer> received = new ArrayList<Integer>();
#Override
public void run() {
System.out.println("Thread has started");
n = 10;
if (counter < n - 1) {
this.id = ThreadLocalRandom.current().nextInt(0, n + 1);
counter++;
}
Message m = new Message(this.id, this.id);
this.mailer.getMap().put(this.id, new ArrayList<Message>());
System.out.println(this.mailer.getMap());
for (int i = 0; i < n; i++) {
if (i == this.id) {
continue;
}
this.mailer.send(i, m);
}
for (int i = 0; i < n; i++) {
if (i == this.id) {
continue;
}
if (this.mailer.getMap().get(i) == null) {
continue;
} else {
this.received.add(this.mailer.readOne(this.id).getContent());
}
}
System.out.println(this.id + "" + this.received);
}
}
This is the Mailer class :
public class Mailer {
private HashMap<Integer, List<Message>> map = new HashMap<>();
public void send(int receiver, Message m) {
synchronized (map) {
while (this.map.get(receiver) == null) {
this.map.get(receiver);
}
if (this.map.get(receiver) == null) {
} else {
map.get(receiver).add(m);
}
}
}
public Message readOne(int receiver) {
synchronized (map) {
if (this.map.get(receiver) == null) {
return null;
} else if (this.map.get(receiver).size() == 0) {
return null;
} else {
Message m = this.map.get(receiver).get(0);
this.map.get(receiver).remove(0);
return m;
}
}
}
public HashMap<Integer, List<Message>> getMap() {
synchronized (map) {
return map;
}
}
}
I have tried so far :
Creating the mailer object inside the run method in agent.
Going by the idea (based on your own answer to this question) that you made the map static, you've made 2 mistakes.
do not use static
static means there is one map for the entire JVM you run this on. This is not actually a good thing: Now you can't create separate mailers on one JVM in the future, and you've made it hard to test stuff.
You want something else: A way to group a bunch of mailer threads together (these are all mailers for the agent), but a bit more discerning than a simple: "ALL mailers in the ENTIRE system are all the one mailer for the one agent that will ever run".
A trivial way to do this is to pass the map in as argument. Alternatively, have the map be part of the agent, and pass the agent to the mailer constructor, and have the mailer ask the agent for the map every time.
this is not thread safe
Thread safety is a crucial concept to get right, because the failure mode if you get it wrong is extremely annoying: It may or may not work, and the JVM is free to base whether it'll work right this moment or won't work on the phase of the moon or the flip of a coin: The JVM is given room to do whatever it feels like it needs to, in order to have a JVM that can make full use of the CPU's powers regardless of which CPU and operating system your app is running on.
Your code is not thread safe.
In any given moment, if 2 threads are both referring to the same field, you've got a problem: You need to ensure that this is done 'safely', and the compiler nor the runtime will throw errors if you fail to do this, but you will get bizarre behaviour because the JVM is free to give you caches, refuse to synchronize things, make ghosts of data appear, and more.
In this case the fix is near-trivial: Use java.util.concurrent.ConcurrentHashMap instead, that's all you'd have to do to make this safe.
Whenever you're interacting with a field that doesn't have a convenient 'typesafe' type, or you're messing with the field itself (one thread assigns a new value to the field, another reads it - you don't do that here, there is just the one field that always points at the same map, but you're messing with the map) - you need to use synchronized and/or volatile and/or locks from the java.util.concurrent package and in general it gets very complicated. Concurrent programming is hard.
I was able to solve this by changing the mailer to static in the Agent class

Garbage collector work with 2 WeakHashMaps

I have cache, implemented with WeakHashMap, like this:
private static WeakHashMap<Object, WeakReference<Object>> objects = new WeakHashMap<>();
I have an instance of class City:
City c = new City();
I now add this instance to my map like this:
objects.put(c, new WeakReference<Object>(c));
According to WeakHashMap jvm implementation, if key doesn't have strong references to it, it's deleted from the map (in its free time).
So, if my object 'c' is not used in the program anymore, it will be deleted from 'objects' map.
So far, so good.
But what happens if I have two maps?
private static WeakHashMap<Object, WeakReference<Object>> objects1 = new WeakHashMap<>();
private static WeakHashMap<Object, WeakReference<Object>> objects2 = new WeakHashMap<>();
City c = new City();
objects1.put(c, new WeakReference<Object>(c));
objects2.put(c, new WeakReference<Object>(c));
Will GC collect the object 'c' in this case?
Take a piece of paper, draw a graph with the objects as vertices, references as edges.
If you can't find a path of strong edges from a GC root (e.g. static field or local variable on the stack) to the object in equestion then it is not strongly reachable and thus eligible for GC.
For Sure it will collect it(when GC starts), because its still referenced by WeakReference and not a Strong reference, not matter how many WeakReferences are referencing it.
You can read about WeakReference here : WeakReference
Here is an Example to demonstrate it:
public class WeakHashMapExample {
//strongly reference key to prevent GC from collecting it
private static final Key stronglyRefKey1 = new Key(1);
public static void main(String[] args) throws InterruptedException {
WeakHashMap<Key, String> cache1 = new WeakHashMap<>();
WeakHashMap<Key, String> cache2 = new WeakHashMap<>();
//adding same keys
Key key2 = new Key(2);
cache1.put(stronglyRefKey1, "val 1");
cache1.put(key2, "val 2");
cache2.put(stronglyRefKey1, "val 1");
cache2.put(key2, "val 2");
key2 = null; // remove strong reference
//may or may not print Key(2) key, depends if GC starts at this point
System.out.println("cache1 = " + cache1);
System.out.println("cache2 = " + cache2);
//for GC to start so all weak reference should be cleared
System.gc();
//after GC ha been ran, key(2) will be removed because its only referenced by weak reference of the WeakHashMap
System.out.println("cache1 = " + cache1);
System.out.println("cache2 = " + cache2);
}
private static class Key{
int value;
private Key(int value) {
this.value = value;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (value != key.value) return false;
return true;
}
#Override
public int hashCode() {
return value;
}
#Override
public String toString() {
return "Key{value=" + value +'}';
}
}
}
Weak is weak, it won't become strong if it is joined by another weak.
It will be garbage collected if no other strong reference. No doubt.

Recursive object deletion

I'm having difficulty making a method that recursively removes all references to objects with a class like:
class CoolObject
{
int someData;
CoolObject[] subObjects; // array and each element get initialized elsewhere
void nullSubObjects()
{
if(subObjects != null)
{
for (int o = 0; o < subObjects.length; o++)
{
if (subObject[o] != null)
{
subObject[o].nullSubObjects(); //returns exception here
subObject[o] = null;
}
}
subObjects = null;
}
}
}
Class elsewhere
{
void update(CoolObject currentObject)
{
//test for creation
if(currentObject.subObject == null && and other conditions)
{
currentObject.subObject = new CoolObject[52];
for(int m = 0; m < 52; m++)
currentObject.subObject[m] = new CoolObject();
}
//test for deletion
if(currentObject.subObject != null && and other conditions)
currentObject.nullSubObjects();
//recursive update
if(currentObject.subObjects != null)
for (int q = 0; q < 52; q++) update(currentObject);
}
}
Whenever I go to update the structure it results in a null pointer exception inside the loop, despite having just checked to make sure the subObject was not null...
Why am I getting this exception?
Without explicitly nullifying it, will the 'someData' prevent their GC?
Thank you all for the feedback!
This implementation was a over complicated means of ensuring subObjects would be more likely to be removed by the automatic GC. I've settled on nullifying the subObjects array at higher levels, and calling System.gc()
Without doing so, my otherwise 40k program would exceed 2Gb before anything was being deleted.
You do not need to do any of this to make an object garbage collectable!
An object becomes garbage when it it no longer accessible, i.e. when it has no accessible references.
Take the following example:
class A {
int i;
}
class B {
A a = new A();
}
class Main {
public static void main(String[] args) {
B b = new B();
// point 1
b = null;
// point 2
}
}
At point 1, an object of type B exists, referred to by b and an object of type A exists, referred to by b.a.
You can imagine a chain of references like this:
+ main
| + object of B
| - object of A
At point 2, I can no longer access the object of type B so it is eligible for garbage collection. I also have no way to access the object of type A, so it will also be garbage collected.
Now the chain of references looks like this:
+ main
| (no references)
+ object of B
| - object of A
Even though the object of type B has a reference to the object of type A, there is still no way to get to that object from main.
An object is garbage collected when there is no chain of references to it that can actually be used by the program.
Why am I getting this exception?
I can think of a couple of reasons why you might get a NPE at that point.
If the network of objects is cyclic, then a CoolObject instance can be reached via a recursive nullSubObjects() call while it is already being processed. Since the recursive call nulls the subObjects field, when you return from the recursion in the middle of the for loop and execute subObject[o].nullSubObjects(); subObject will be null and you will get an NPE.
If some other thread is updating the graph while you are doing this, all bets are off. This code has no synchronization.
I'd also note that this is not the real code (it is no compilable). The real problem may be something that only exists on the real code.
Is there a safer way to ensure all references are removed?
This should avoid the first problem:
if (subObjects != null)
{
CoolObject[] s = subObjects;
subObjects = null;
for (int o = 0; o < s.length; o++)
{
if (s[o] != null)
{
s[o].nullSubObjects(); //returns exception here
s[o] = null;
}
}
}
However, this is probably unnecessary away; see below.
Without explicitly nullifying it, will the 'someData' prevent their GC?
The whole tree / graph will be garbage collected once all existing references to it become unreachable. It is hard to imagine a situation where it necessary to null all of the references like this.

JIT Optimization and Weak References

I have the following piece of code:
private final List<WeakReference<T>> slaves;
public void updateOrdering() {
// removes void weak references
// and ensures that weak references are not voided
// during subsequent sort
List<T> unwrapped = unwrap();
assert unwrapped.size() == this.slaves.size();
// **** could be reimplemented without using unwrap() ****
Collections.sort(this.slaves, CMP_IDX_SLV);
unwrapped = null;// without this, ....
}
Method unwrap() just creates a list of T's referenced by the weak references in slaves
and as a side effect eliminates the weak references referencing null in slaves.
Then comes the sort which relies on that each member of slaves references some T;
otherwise the code yields a NullPointerException.
Since unwrapped holds a reference on each T in slaves, during sorting no GC eliminates a T. Finally, unwrapped = null eliminates the reference on unwrapped
and so releases GC again. Seems to work quite well.
Now my question:
If I remove unwrapped = null; this results in NullPointerExceptions when running many tests under some load. I suspect that the JIT eliminates List<T> unwrapped = unwrap();
and so GC applies to the T's in slaves during sorting.
Do you have another explanation? If you agree with me, is this a bug in the JIT?
I personally think that unwrapped = null should not be necessary, because unwrapped is removed from the frame as soon as updateOrdering() returns. Is there a specification what may be optimized and what is not?
Or did I do the thing in the wrong way? I have the idea to modify comparator that it allows weak references on null. What do you think about that?
Thanks for suggestions.
Add on (1)
Now I want to add some missing pieces of information:
First of all Java version:
java version "1.7.0_45"
OpenJDK Runtime Environment (IcedTea 2.4.3) (suse-8.28.3-x86_64)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)
Then someone wanted to see method unwrap
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
T cand;
WeakReference<T> slvRef;
Iterator<WeakReference<T>> iter = this.slaves.iterator();
while (iter.hasNext()) {
slvRef = iter.next();
cand = slvRef.get();
if (cand == null) {
iter.remove();
continue;
}
assert cand != null;
res.add(cand);
} // while (iter.hasNext())
return res;
}
Note that while iterating, void references are removed.
In fact i replaced this method by
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
for (T cand : this) {
assert cand != null;
res.add(cand);
}
return res;
}
using my own iterator but functionally this should be the same.
Then someone wantet the stacktrace. Here is a piece of it.
java.lang.NullPointerException: null
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:44)
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:40)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:324)
at java.util.TimSort.sort(TimSort.java:189)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
at WeakSlaveCollection.updateOrdering(WeakSlaveCollection.java:183)
it points into the comparator, the line with the return.
static class IdxComparator
implements Comparator<WeakReference<? extends XSlaveNumber>> {
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
return slv2.get().index()-slv1.get().index();
}
} // class IdxComparator
and finally,
private final static IdxComparator CMP_IDX_SLV = new IdxComparator();
is an important constant.
Add on (2)
Observed now that indeed NPE occurs even if 'unwrapped = null' is present in updateOrdering().
Weak references may be removed by java runtime
if no strict reference holds after jit optimization.
The source code seems not important at all.
I solved the problem the following way:
public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}
without any decoration inserted to prevent slaves to be garbage collected
and the comparator in CMP_IDX_SLV enabled to handle weak references to null:
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
XSlaveNumber sSlv1 = slv1.get();
XSlaveNumber sSlv2 = slv2.get();
if (sSlv1 == null) {
return sSlv2 == null ? 0 : -1;
}
if (sSlv2 == null) {
return +1;
}
assert sSlv1 != null && sSlv2 != null;
return sSlv2.index()-sSlv1.index();
}
As a side effect, ordering the underlying list List> slaves;
puts the void weak references at the end of the list, where it can be collected later.
I examine your source code, and I got NullPointerException when JIT compile my method corresponding to your method "updateOrdering" and GC occurs during sorting.
But I got NullPointerException when Collections.sort whether with or without unwrapped = null.
This maybe occurs difference between my sample source code and yours, or Java version difference. I will examine if you tell Java version.
I use java below version.
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)
If you want to cheat on JIT compilation, the below code insert your source code instead unwrapped = null(e.g.). Then, JIT compilation doesn't eliminates unwrapped code.
long value = unwrapped.size() * unwrapped.size();
if(value * value % 3 == 1) {
//Because value * value % 3 always is 1 or 0, this code can't reach.
//Insert into this the source code that use unwrapped array, for example, show unwrapped array.
}
My examination result is below.
If JIT don't optimize my method corresponding to updateOrdering, no NullPointerException occurs.
If JIT optimize my method, then NullPointerException occurs at some point.
If JIT optimize my method inserting the above source code cheating JIT compiler, then no NullPointerException occurs.
So, I(and you) suggest JIT optimze eliminates unwrapped code, then NullPointerException occurs.
By the way, if you want to show JIT compiler optimization, you invoke java with -XX:+PrintCompilation.
If you want to show GC, with -verbose:gc.
Just for information, my sample source code is below.
public class WeakSampleMain {
private static List<WeakReference<Integer>> weakList = new LinkedList<>();
private static long sum = 0;
public static void main(String[] args) {
System.out.println("start");
int size = 1_000_000;
for(int i = 0; i < size; i++) {
Integer value = Integer.valueOf(i);
weakList.add(new WeakReference<Integer>(value));
}
for(int i = 0; i < 10; i++) {
jitSort();
}
GcTask gcTask = new GcTask();
Thread thread = new Thread(gcTask);
thread.start();
for(int i = 0; i < 100000; i++) {
jitSort();
}
thread.interrupt();
System.out.println(sum);
}
public static void jitSort() {
List<Integer> unwrappedList = unwrapped();
removeNull();
Collections.sort(weakList,
new Comparator<WeakReference<Integer>>() {
#Override
public int compare(WeakReference<Integer> o1,
WeakReference<Integer> o2) {
return Integer.compare(o1.get(), o2.get());
}
}
);
for(int i = 0; i < Math.min(weakList.size(), 1000); i++) {
sum += weakList.get(i).get();
}
unwrappedList = null;
// long value = (sum + unwrappedList.size());
// if((value * value) % 3 == 2) {
// for(int i = 0; i < unwrappedList.size(); i++) {
// System.out.println(unwrappedList.get(i));
// }
// }
}
public static List<Integer> unwrapped() {
ArrayList<Integer> list = new ArrayList<Integer>();
for(WeakReference<Integer> ref : weakList) {
Integer i = ref.get();
if(i != null) {
list.add(i);
}
}
return list;
}
public static void removeNull() {
Iterator<WeakReference<Integer>> itr = weakList.iterator();
while(itr.hasNext()) {
WeakReference<Integer> ref = itr.next();
if(ref.get() == null) {
itr.remove();
}
}
}
public static class GcTask implements Runnable {
private volatile int result = 0;
private List<Integer> stockList = new ArrayList<Integer>();
public void run() {
while(true) {
if(Thread.interrupted()) {
break;
}
int size = 1000000;
stockList = new ArrayList<Integer>(size);
for(int i = 0; i < size; i++) {
stockList.add(new Integer(i));
}
if(System.currentTimeMillis() % 1000 == 0) {
System.out.println("size : " + stockList.size());
}
}
}
public int getResult() {
return result;
}
}
}
As of Java 9, the correct way to prevent the JIT from discarding unwrapped is to use Reference.reachabilityFence:
public void updateOrdering() {
List<T> unwrapped = unwrap();
Collections.sort(this.slaves, CMP_IDX_SLV);
Reference.reachabilityFence(unwrapped);
}
The presence of the reachabilityFence call causes unwrapped to be considered strongly reachable before the call, preventing collection of unwrapped or its elements until the sort completes. (The strange way in which reachabilityFence's effects seem to propagate backward in time is because it behaves primarily as a JIT directive.) Without reachabilityFence, unwrapped can be collected once the JIT can prove it will never again be accessed, even though the variable is still in scope.
Your question
If I remove unwrapped = null; this results in NullPointerException when running many tests under some load.
According to my understanding I do not think so that unwrapped = null; makes any difference.
Yes, I have also read that making objects = null sometime increases the probability the object referenced will be GC'ed but I don't think it matters here because once the method ends, scope of unwrapped ends and is eligible for GC'ed and in your function sorting Collections.sort(this.slaves, CMP_IDX_SLV); is done prior to unwrapped = null; so it make no sense the you get NPE when adding or removing them.
I think it is just a coincidence that you get NPE, I believe if you run the test again you will get NPE with that statement also.
If you read Java Documentation
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.
Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.
So it is really possible when you constructed the List from unwrap() some objects might have been marked finalized and while your Collection.sort is working some WeakRefrence are assigned null. And the point stated by Mattias Buelens is perfectly valid you'll always lose in a fight against the compiler.
If you agree with me, is this a bug in the JIT?
No surely not, I completely disagree with you.
I have the idea to modify comparator that it allows weak references on null. What do you think about that?
I think it will solve your one problem of NPE but your requirement removes void weak references and ensures that weak references are not voided during subsequent sort is not satisfied.
Rather try to call unwrap once again, this will reduce the window for NPE to almost zero,
List<T> unwrapped = unwrap();
unwrapped = unwrap(); //Again to eliminate the chances for NPE as now we would have
//already made strong refrences to all objects which have not been `null`

Create and put a map value only if not already present, and get it: thread-safe implementation

What is the best way to make this snippet thread-safe?
private static final Map<A, B> MAP = new HashMap<A, B>();
public static B putIfNeededAndGet(A key) {
B value = MAP.get(key);
if (value == null) {
value = buildB(...);
MAP.put(key, value);
}
return value;
}
private static B buildB(...) {
// business, can be quite long
}
Here are the few solutions I could think about:
I could use a ConcurrentHashMap, but if I well understood, it just makes the atomic put and get operations thread-safe, i.e. it does not ensure the buildB() method to be called only once for a given value.
I could use Collections.synchronizedMap(new HashMap<A, B>()), but I would have the same issue as the first point.
I could set the whole putIfNeededAndGet() method synchronized, but I can have really many threads accessing this method together, so it could be quite expensive.
I could use the double-checked locking pattern, but there is still the related out-of-order writes issue.
What other solutions may I have?
I know this is a quite common topic on the Web, but I didn't find a clear, full and working example yet.
Use ConcurrentHashMap and the lazy init pattern which you used
public static B putIfNeededAndGet(A key) {
B value = map.get(key);
if (value == null) {
value = buildB(...);
B oldValue = map.putIfAbsent(key, value);
if (oldValue != null) {
value = oldValue;
}
}
return value;
}
This might not be the answer you're looking for, but use the Guava CacheBuilder, it already does all that and more:
private static final LoadingCache<A, B> CACHE = CacheBuilder.newBuilder()
.maximumSize(100) // if necessary
.build(
new CacheLoader<A, B>() {
public B load(A key) {
return buildB(key);
}
});
You can also easily add timed expiration and other features as well.
This cache will ensure that load() (or in your case buildB) will not be called concurrently with the same key. If one thread is already building a B, then any other caller will just wait for that thread.
In the above solution it is possible that many threads will class processB(...) simultaneously hence all will calculate. But in my case i am using Future and a single thread only get the old value as null hence it will only compute the processB rest will wait on f.get().
private static final ConcurrentMap<A, Future<B>> map = new ConcurrentHashMap<A, Future<B>>();
public static B putIfNeededAndGet(A key) {
while (true) {
Future<V> f = map.get(key);
if (f == null) {
Callable<B> eval = new Callable<V>() {
public B call() throws InterruptedException {
return buildB(...);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = map.putIfAbsent(arg, ft);
if (f == null) {
f = ft;
ft.run();
}
}
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
} catch (ExecutionException e) {
}
}
}
Thought maybe this will be useful for someone else as well, using java 8 lambdas I created this function which worked great for me:
private <T> T getOrCreate(Object key, Map<Object, T> map,
Function<Object, T> creationFunction) {
T value = map.get(key);
// if the doesn't exist yet - create and add it
if (value == null) {
value = creationFunction.apply(key);
map.put(label, metric);
}
return value;
}
then you can use it like this:
Object o = getOrCreate(key, map, s -> createSpecialObjectWithKey(key));
I created this for something specific but changed the context and code to a more general look, that is why my creationFunction has one parameter, it can also have no parameters...
also you can generify it more by changing Object to a generic type, if it's not clear let me know and I'll add another example.
UPDATE:
I just found out about Map.computeIfAbsent which basically does the same, gotta love java 8 :)

Categories

Resources