org.apache.wink.common.internal.utils.SoftConcurrentMap Maven / Gradle / Ivy
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.wink.common.internal.utils;
import java.lang.ref.SoftReference;
import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Concurrent implementation of the SimpleMap interface. This implementation is
* intend to be used by soft caches, therefore the keys are kept in WeakHashMap,
* while the value is kept using the SoftReference.
*
* Pay attention that put value always returns the current value and not the
* original value. It was done to allow the following code pattern:
*
*
* SoftConcurrentMap<K, V> cache = new SoftConcurrentMap<K, V>();
* V cached = cache.get(key);
* return cached = !null ? cached : cache.put(createValue());
*
*
* @param
* @param
*/
public class SoftConcurrentMap implements SimpleMap {
/*
* Note that this volatile variable is important for publication purposes.
*/
private volatile Map> map;
/**
* Provides the map implementation.
*
* @param map
*/
public SoftConcurrentMap() {
this.map = new WeakHashMap>();
}
public V get(K key) {
SoftReference softReference = map.get(key);
return softReference != null ? softReference.get() : null;
}
/**
* Associates the specified value with the specified key in this map. If the
* map previously contained a mapping for this key, the old value is
* replaced by the specified value.
*
* Unlike the regular Map.put method, this method returns the current value
* and not the previous value.
*
* Note that a copy on write pattern is used where the existing cache is
* treated as a read only cache and then it is copied to a new cache.
*
* @return val - the current value.
*/
public synchronized V put(K key, V val) {
/*
* under the WeakHashMap(Map) constructor, java.util.AbstractMap.putAll is called, which uses
* an iterator. Iterators, as we all know, are not thread-safe; they are susceptible
* to ConcurrentModificationExceptions. Note that this method is already 'synchronized'.
* However, that does not protect this.map from the silent garbage collector thread, which
* may remove something at any time due to the internal values being "SoftReferences".
* Instead of synchronizing on this.map, let's just catch ConcurrentModificationException
* ignore it, and retry in the while loop.
*/
// non-null to avoid NPE when the timing of multi-threaded runs conspire against us
WeakHashMap> copyOfMap = new WeakHashMap>();
boolean complete = false;
while (!complete) {
try {
copyOfMap = new WeakHashMap>(map);
complete = true;
} catch (ConcurrentModificationException e) {
// ignored
}
}
copyOfMap.put(key, new SoftReference(val));
map = copyOfMap;
return val;
}
public synchronized void clear() {
/*
* Note that a copy on write pattern is used here so that the cache is
* cleared by assigning to a new empty cache.
*/
map = new WeakHashMap>();
}
}