Java Concurrency - java

I'm trying to implement some kind of accumulation logic in a multi threaded environment; I’m wondering is there any better/faster way to do it without the lock and synchronized keyword? The following is my current code:
public class ConcurrentHashMapTest {
private static final int NB_THREADS = 1_000;
private final Map<String, Integer> cnts = new HashMap<>();
private static final Lock RWLOCK = new ReentrantLock(true);
private static final String[] NAMES = {
"A", "B"
};
public void testIt() {
ExecutorService executor =
Executors.newFixedThreadPool(NB_THREADS);
for (int i = 0; i < NB_THREADS; i++) {
Runnable task = new WorkerThread();
executor.submit(task);
}
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(cnts);
}
private void accumulate(String name) {
RWLOCK.lock();
try {
Integer cnt = cnts.get(name);
if (cnt == null) {
cnts.put(name, 1);
} else {
cnts.put(name, cnt + 1);
}
} finally {
RWLOCK.unlock();
}
}
private class WorkerThread implements Runnable {
#Override
public void run() {
accumulate(NAMES[ThreadLocalRandom.current().nextInt(0, NAMES.length)]);
}
}
}

Java 8:
private final Map<String, AtomicInteger> cnts =
new ConcurrentHashMap<>();
private void accumulate(String name) {
cnts.computeIfAbsent(name, k -> new AtomicInteger()).incrementAndGet();
}
The ConcurrentHashMap can be freely accessed from multiple threads. The computeIfAbsent method takes a lambda to evaluate to get a value for the key if the key is not present in the map, and adds it if and only if there is no such mapping, and then returns that value. It's effectively putIfAbsent followed by get. The value is a new AtomicInteger with the value 0. Whether there was an existing value, or whether a new one with value 0 was just added, in either case increment it.
Java 7:
private final ConcurrentMap<String, AtomicInteger> cnts =
new ConcurrentHashMap<>();
private void accumulate(String name) {
cnts.putIfAbsent(name, new AtomicInteger());
cnts.get(name).incrementAndGet();
}
For Java 7, there is no computeIfAbsent method, but that effectively just does a putIfAbsent followed by a get, so the same effect is achieved by calling those methods. There is no concern that the value already existed in the map; a new, zero AtomicInteger is added if and only if the map had no value for that key. Even if another thread got in there before us and added a zero, both threads would then see and increment that same AtomicInteger instance.

use a concurrent hash map with String and AtomicInteger. Both are thread safe and thus can be used freely.

I'd be wary of using fairness on your ReentrantLock in this case, as there's no benefit to your accumulator if longer waiting threads get access first. Take a look at Brian Goetz's 'Java Concurrency in Practice'
Why wouldn't we want to make all locks fair? After all, fairness is good and unfairness is bad, right? (It's not accidental that whenever kids want to appeal a decision, "that's not fair" almost certainly comes up. We think fairness is pretty important, and they know it.) In reality, the fairness guarantee for locks is a very strong one, and comes at a significant performance cost. The bookkeeping and synchronization required to ensure fairness mean that contended fair locks will have much lower throughput than unfair locks. As a default, you should set fair to false unless it is critical to the correctness of your algorithm that threads be serviced in exactly the order they queued up.

You could use a Map of name to AtomicInteger and use double-check locking when there is no counter in the map at all. Be aware that you need to use the volatile keyword for effective double-check locking.
This way you will only lock the whole map for actually adding brand new entries, the rest of the processing can happen in parallel.
You risk massively over-complicating your program here though and possibly even reducing performance in real-world cases. Is contention on this map really a performance bottle-neck?

According to Oracle Java 7 API : implementation of HashMap is not synchronized.
You can use Hashtable implementation or declare : private final Map<String, Integer> cnts = Collections.synchronizedMap(new HashMap<String, Integer>());

I think what you are looking for is a Multiton:
/**
* Holds a thread-safe map of unique create-once items.
*
* Contract:
*
* Only one object will be made for each key presented.
*
* Thread safe.
*
* #author OldCurmudgeon
* #param <K>
* #param <V>
*/
public class Multiton<K, V> {
// Map from the key to the futures of the items.
private final ConcurrentMap<K, Future<V>> multitons = new ConcurrentHashMap<>();
// The creator can create an item of type V.
private final Creator<K, V> creator;
public Multiton(Creator<K, V> creator) {
this.creator = creator;
}
/**
* There can be only one.
*
* Use a FutureTask to do the creation to ensure only one construction.
*
* #param key
* #return
* #throws InterruptedException
* #throws ExecutionException
*/
public V get(final K key) throws InterruptedException, ExecutionException {
// Already made?
Future<V> f = multitons.get(key);
if (f == null) {
// Plan the future but do not create as yet.
FutureTask<V> ft = new FutureTask<>(() -> creator.create(key));
// Store it.
f = multitons.putIfAbsent(key, ft);
if (f == null) {
// It was successfully stored - it is the first (and only)
f = ft;
// Make it happen.
ft.run();
}
}
// Wait for it to finish construction and return the constructed.
return f.get();
}
/**
* Returns a Map indicating the current state.
*
* #return a Map which should reflect the current state.
*
* #throws java.lang.InterruptedException
* #throws java.util.concurrent.ExecutionException
*/
public Map<K, V> getMap() throws InterruptedException, ExecutionException {
Map<K, V> map = new HashMap<>();
for (Map.Entry<K, Future<V>> e : multitons.entrySet()) {
map.put(e.getKey(), e.getValue().get());
}
return map;
}
/**
* User provides one of these to do the construction.
*
* #param <K>
* #param <V>
*/
public abstract static class Creator<K, V> {
// Return a new item under the key.
abstract V create(K key) throws ExecutionException;
}
}
Usage - for demonstration - adds up all integers up to 999, keying on their first digit:
Multiton<String, AtomicInteger> counts = new Multiton<>(
new Creator<String, AtomicInteger>() {
#Override
AtomicInteger create(String key) throws ExecutionException {
return new AtomicInteger();
}
}
);
public void test() throws InterruptedException, ExecutionException {
for (int i = 0; i < 1000; i++) {
counts.get(Integer.toString(i).substring(0, 1)).addAndGet(i);
}
System.out.println(counts.getMap());
}
Prints:
{0=0, 1=15096, 2=25197, 3=35298, 4=45399, 5=55500, 6=65601, 7=75702, 8=85803, 9=95904}
Java < 8 version:
/**
* Holds a thread-safe map of unique create-once items.
*
* Contract:
*
* Only one object will be made for each key presented.
*
* Thread safe.
*
* #author OldCurmudgeon
* #param <K>
* #param <V>
*/
public class Multiton<K, V> {
// Map from the key to the futures of the items.
private final ConcurrentMap<K, Future<V>> multitons = new ConcurrentHashMap<>();
// The creator can create an item of type V.
private final Creator<K, V> creator;
public Multiton(Creator<K, V> creator) {
this.creator = creator;
}
/**
* There can be only one.
*
* Use a FutureTask to do the creation to ensure only one construction.
*
* #param key
* #return
* #throws InterruptedException
* #throws ExecutionException
*/
public V get(final K key) throws InterruptedException, ExecutionException {
// Already made?
Future<V> f = multitons.get(key);
if (f == null) {
// Plan the future but do not create as yet.
FutureTask<V> ft = new FutureTask<>(new Callable<V>() {
#Override
public V call() throws Exception {
// Doing this inline may be a little contrived but it maintains the linkage with the Java-8 version.
return creator.create(key);
}
}
);
// Store it.
f = multitons.putIfAbsent(key, ft);
if (f == null) {
// It was successfully stored - it is the first (and only)
f = ft;
// Make it happen.
ft.run();
}
}
// Wait for it to finish construction and return the constructed.
return f.get();
}
/**
* Returns a Map indicating the current state.
*
* #return a Map which should reflect the current state.
*
* #throws java.lang.InterruptedException
* #throws java.util.concurrent.ExecutionException
*/
public Map<K, V> getMap() throws InterruptedException, ExecutionException {
Map<K, V> map = new HashMap<>();
for (Map.Entry<K, Future<V>> e : multitons.entrySet()) {
map.put(e.getKey(), e.getValue().get());
}
return map;
}
/**
* User provides one of these to do the construction.
*
* #param <K>
* #param <V>
*/
public abstract static class Creator<K, V> {
// Return a new item under the key.
abstract V create(K key) throws ExecutionException;
}
}

Related

Optimize insertion from ArrayList to HashMap

I'm trying to insert data from ArrayList to HashMap<String, Language> optimally.
Many items may have the same languge_name (code below), so I need to group items having the same language in Language class and store languages in a HashMap with the name of the language as a Key.
Item
String name;
String language_name;
Language
String language_name;
int numberItems;
LinkedList<String> Items;
I solved this as follows:
ArrayList<Item> items; // given array of items
HashMap<String, Language> languages = new HashMap<String, Language>();
items.forEach(item -> {
/** case 1: language isn't specified */
if (item.getLanguageName() == null) {
item.setLanguageName("unknown");
}
/** case 2: language already added */
if (languages.containsKey(item.getLanguageName())) {
languages.get(item.getLanguageName()).getItems().add(item.getName());
languages.get(item.getLanguageName())
.setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
} else {
/** case 3: language isn't added yet */
LinkedList<String> languageItems = new LinkedList<String>();
languageItems.add(item.getName());
Language language = new Language(item.getLanguageName(), 1, languageItems);
languages.put(item.getLanguageName(), language);
}
});
Any help would be appreciated!
Assuming you're using Java 8 or later, this can be accomplished nicely with built-in stream functions.
HashMap<String, List<Items>> itemsGroupedByLanguage =
items.stream().collect(Collectors.groupingBy(Items::getLanguage));
tl;dr
It's not possible to achieve what you desire using Java (8+) inbuilt collector, but you can write your own custom collector and write code like below to collect into a map as -
Map<String, Language> languages = items.stream().collect(LanguageCollector.toLanguage());
Let's first look at Collector<T, A, R> interface
public interface Collector<T, A, R> {
/**
* A function that creates and returns a new mutable result container.
*/
Supplier<A> supplier();
/**
* A function that folds a value into a mutable result container.
*/
BiConsumer<A, T> accumulator();
/**
* A function that accepts two partial results and merges them. The
* combiner function may fold state from one argument into the other and
* return that, or may return a new result container.
*/
BinaryOperator<A> combiner();
/**
* Perform the final transformation from the intermediate accumulation type
*/
Function<A, R> finisher();
/**
* Returns a Set of Collector.Characteristics indicating
* the characteristics of this Collector. This set should be immutable.
*/
Set<Characteristics> characteristics();
}
Where T is the generic type of the items in the stream to be collected.
A is the type of the accumulator, the object on which the partial result will be accumulated during the collection process.
R is the type of the object (typically, but not always, the collection) resulting
from the collect operation
Now let's look at the custom LanguageCollector
public class LanguageCollector
implements Collector<Item, Map<String, Language>, Map<String, Language>> {
/**
* The supplier method has to return a Supplier of an empty accumulator - a parameterless
* function that when invoked creates an instance of an empty accumulator used during the
* collection process.
*/
#Override
public Supplier<Map<String, Language>> supplier() {
return HashMap::new;
}
/**
* The accumulator method returns the function that performs the reduction operation. When
* traversing the nth element in the stream, this function is applied with two arguments, the
* accumulator being the result of the reduction (after having collected the first n–1 items of
* the stream) and the nth element itself. The function returns void because the accumulator is
* modified in place, meaning that its internal state is changed by the function application to
* reflect the effect of the traversed element
*/
#Override
public BiConsumer<Map<String, Language>, Item> accumulator() {
return (map, item) -> {
if (item.getLanguageName() == null) {
item.setLanguageName("unknown");
} else if (map.containsKey(item.getLanguageName())) {
map.get(item.getLanguageName()).getItems().add(item.getName());
map.get(item.getLanguageName())
.setNumberItems(map.get(item.getLanguageName()).getNumberItems() + 1);
} else {
Language language = new Language(item.getLanguageName(), 1);
language.add(item.getName());
map.put(item.getLanguageName(), language);
}
};
}
/**
* The combiner method, return a function used by the reduction operation, defines how the
* accumulators resulting from the reduction of different subparts of the stream are combined
* when the subparts are processed in parallel
*/
#Override
public BinaryOperator<Map<String, Language>> combiner() {
return (map1, map2) -> {
map1.putAll(map2);
return map1;
};
}
/**
* The finisher() method needs to return a function which transforms the accumulator to the
* final result. In this case, the accumulator is the final result as well. Therefore it is
* possible to return the identity function
*/
#Override
public Function<Map<String, Language>, Map<String, Language>> finisher() {
return Function.identity();
}
/**
* The characteristics, returns an immutable set of Characteristics, defining the behavior of
* the collector—in particular providing hints about whether the stream can be reduced in
* parallel and which optimizations are valid when doing so
*/
#Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(
EnumSet.of(Characteristics.IDENTITY_FINISH));
}
/**
* Static method to create LanguageCollector
*/
public static LanguageCollector toLanguage() {
return new LanguageCollector();
}
}
I have modified your classes at little bit to (to follow the naming convention and more for readable accumulator operation).
Class Item
public class Item {
private String name;
private String languageName;
public Item(String name, String languageName) {
this.name = name;
this.languageName = languageName;
}
//Getter and Setter
}
Class Language
public class Language {
private String languageName;
private int numberItems;
private LinkedList<String> items;
public Language(String languageName, int numberItems) {
this.languageName = languageName;
this.numberItems = numberItems;
items = new LinkedList<>();
}
public void add(String item) {
items.add(item);
}
// Getter and Setter
public String toString() {
return "Language(languageName=" + this.getLanguageName() + ", numberItems=" + this.getNumberItems() + ", items=" + this.getItems() + ")";
}
}
Running code
public static void main(String[] args) {
List<Item> items =
Arrays.asList(
new Item("ItemA", "Java"),
new Item("ItemB", "Python"),
new Item("ItemC", "Java"),
new Item("ItemD", "Ruby"),
new Item("ItemE", "Python"));
Map<String, Language> languages = items.stream().collect(LanguageCollector.toLanguage());
System.out.println(languages);
}
prints
{Java=Language(languageName=Java, numberItems=2, items=[ItemA, ItemC]), Ruby=Language(languageName=Ruby, numberItems=1, items=[ItemD]), Python=Language(languageName=Python, numberItems=2, items=[ItemB, ItemE])}
For more information please read book 'Modern Java in Action: Lambdas, streams, functional and reactive programming' chapter 6.5 or check this link

Is there an expiring map in Java that expires elements after a period of time since *first* insertion?

I tried looking at cache mechanisms, such as Guava's Cache. Their expiration is only since last update.
What I'm looking for is a data structure that stores keys and cleans the keys after a time has passed since the first insertion. I'm planning for the value to be some counter.
A scenario might be a silent worker that does some work for the first time but keeps silent for an expiry period of time - even if work is requested. If work is requested after the expiry time has passed, he'll do the work.
Know of such a data structure? Thanks.
There are a few options for this.
Passive Removal
If it is not a requirement to clean up the expired keys as soon as they expire or at set intervals (i.e. a key does not need to be removed when the key expires or at some set interval), then PassiveExpiringMap from Apache Commons Collections is a good option. When attempting to access a key in this map, the Time to Live (TTL) of the key (all keys have the same TTL) is checked and if the key has expired, it is removed from the map and null is returned. This data structure does not have an active clean-up mechanism, so expired entries are only removed when they are accessed after the TTL corresponding to the key has expired.
Cache
If more cache-based functionality is needed (such as maximum cache capacity and add/remove listening), Google Guava provides the CacheBuilder class. This class is more complex than the Apache Commons alternative, but it also provides much more functionality. The trade-off may be worth it if this intended for more of a cache-based application.
Threaded Removal
If active removal of expired keys is needed, a separate thread can be spawn that is responsible for removing expired keys. Before looking at a possible implementation structure, it should be noted that this approach may be less performant than the above alternatives. Besides the overhead of starting a thread, the thread may cause contention with clients accessing the map. For example, if a client wants to access a key and the clean-up thread is currently removing expired keys, the client will either block (if synchronization is used) or have a different view of the map (which key-value pairs are contained in the map) if some concurrent mechanism is employed.
With that said, using this approach is complicated because it requires that the TTL be stored with the key. One approach is to create an ExpiringKey, such as (each key can have its own TTL; even if all of the keys will end up having the same TTL value, this technique removes the need to create a Map decorator or some other implementation of the Map interface):
public class ExpiringKey<T> {
private final T key;
private final long expirationTimestamp;
public ExpiringKey(T key, long ttlInMillis) {
this.key = key;
expirationTimestamp = System.currentTimeMillis() + ttlInMillis;
}
public T getKey() {
return key;
}
public boolean isExpired() {
return System.currentTimeMillis() > expirationTimestamp;
}
}
Now the type of the map would be Map<ExpiringKey<K>, V> with some specific K and V type values. The background thread can be represented using a Runnable that resembles the following:
public class ExpiredKeyRemover implements Runnable {
private final Map<ExpiringKey<?>, ?> map;
public ExpiredKeyRemover(Map<ExpiringKey<?>, ?> map) {
this.map = map;
}
#Override
public void run() {
Iterator<ExpiringKey<?>> it = map.keySet().iterator();
while (it.hasNext()) {
if (it.next().isExpired()) {
it.remove();
}
}
}
}
Then the Runnable can be started so that it executes at a fixed interval using a ScheduledExecutorService as follows (which will clean up the map every 5 seconds):
Map<ExpiringKey<K>, V> myMap = // ...
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(new ExpiredKeyRemover(myMap), 0, 5, TimeUnit.SECONDS);
It is important to note that the Map implementation used for myMap must be synchronized or allow for concurrent access. The challenge with a concurrent Map implementation is that the ExpiredKeyRemover may see a different view of the map than a client and an expired key may be returned to the client if the clean-up thread is not completed removing other keys (even if it has removed the desired/expired key since its changes may not have been committed yet). Additionally, the above key-removal code can be implemented using streams, but the above code has been used just to illustrate the logic rather than provide a performant implementation.
Hope that helps.
ExpiringMap
You can use ExpiringMap. This will remove element from map after specified time while initializing the Map . Here is the syntax
public static Map<String, Long> threatURLCacheMap = ExpiringMap.builder().expiration(5, TimeUnit.MINUTES).build();
This will create a Map in which each element will expire 5 minutes of insertion.
You can use this dependencies into your maven project net.jodah.expiringmap.
here is link to learn about it more
https://crunchify.com/how-to-use-expiringmap-maven-java-utility-to-remove-expired-objects-from-map-automatically-complete-java-tutorial/
Created a data structure. Called it DuplicateActionFilterByInsertTime.
The correct notion is filtering duplicate messages. The following class filters from insert time for some period (filterMillis).
Implementation:
public class DuplicateActionFilterByInsertTime<E extends Runnable> {
private static final Logger LOGGER = Logger.getLogger(DuplicateActionFilterByInsertTime.class.getName());
private final long filterMillis;
private final ConcurrentHashMap<E, SilenceInfoImpl> actionMap = new ConcurrentHashMap<>();
private final ConcurrentLinkedQueue<E> actionQueue = new ConcurrentLinkedQueue<>();
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private final AtomicBoolean purgerRegistered = new AtomicBoolean(false);
private final Set<Listener<E>> listeners = ConcurrentHashMap.newKeySet();
public DuplicateActionFilterByInsertTime(int filterMillis) {
this.filterMillis = filterMillis;
}
public SilenceInfo get(E e) {
SilenceInfoImpl insertionData = actionMap.get(e);
if (insertionData == null || insertionData.isExpired(filterMillis)) {
return null;
}
return insertionData;
}
public boolean run(E e) {
actionMap.computeIfPresent(e, (e1, insertionData) -> {
int count = insertionData.incrementAndGet();
if (count == 2) {
notifyFilteringStarted(e1);
}
return insertionData;
});
boolean isNew = actionMap.computeIfAbsent(e, e1 -> {
SilenceInfoImpl insertionData = new SilenceInfoImpl();
actionQueue.add(e1);
return insertionData;
}).getCount() == 1;
tryRegisterPurger();
if (isNew) {
e.run();
}
return isNew;
}
private void tryRegisterPurger() {
if (actionMap.size() != 0 && purgerRegistered.compareAndSet(false, true)) {
scheduledExecutorService.schedule(() -> {
try {
for (Iterator<E> iterator = actionQueue.iterator(); iterator.hasNext(); ) {
E e = iterator.next();
SilenceInfoImpl insertionData = actionMap.get(e);
if (insertionData == null || insertionData.isExpired(filterMillis)) {
iterator.remove();
}
if (insertionData != null && insertionData.isExpired(filterMillis)) {
SilenceInfoImpl removed = actionMap.remove(e);
FilteredItem<E> filteredItem = new FilteredItem<>(e, removed);
notifySilenceFinished(filteredItem);
} else {
// All the elements that were left shouldn't be purged.
break;
}
}
} finally {
purgerRegistered.set(false);
tryRegisterPurger();
}
}, filterMillis, TimeUnit.MILLISECONDS);
}
}
private void notifySilenceFinished(FilteredItem<E> filteredItem) {
new Thread(() -> listeners.forEach(l -> {
try {
l.onFilteringFinished(filteredItem);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Purge notification failed. Continuing to next one (if exists)", e);
}
})).start();
}
private void notifyFilteringStarted(final E e) {
new Thread(() -> listeners.forEach(l -> {
try {
l.onFilteringStarted(e);
} catch (Exception e1) {
LOGGER.log(Level.WARNING, "Silence started notification failed. Continuing to next one (if exists)", e1);
}
})).start();
}
public void addListener(Listener<E> listener) {
listeners.add(listener);
}
public void removeLister(Listener<E> listener) {
listeners.remove(listener);
}
public interface SilenceInfo {
long getInsertTimeMillis();
int getCount();
}
public interface Listener<E> {
void onFilteringStarted(E e);
void onFilteringFinished(FilteredItem<E> filteredItem);
}
private static class SilenceInfoImpl implements SilenceInfo {
private final long insertTimeMillis = System.currentTimeMillis();
private AtomicInteger count = new AtomicInteger(1);
int incrementAndGet() {
return count.incrementAndGet();
}
#Override
public long getInsertTimeMillis() {
return insertTimeMillis;
}
#Override
public int getCount() {
return count.get();
}
boolean isExpired(long expirationMillis) {
return insertTimeMillis + expirationMillis < System.currentTimeMillis();
}
}
public static class FilteredItem<E> {
private final E item;
private final SilenceInfo silenceInfo;
FilteredItem(E item, SilenceInfo silenceInfo) {
this.item = item;
this.silenceInfo = silenceInfo;
}
public E getItem() {
return item;
}
public SilenceInfo getSilenceInfo() {
return silenceInfo;
}
}
}
Test example: (More tests here)
#Test
public void testSimple() throws InterruptedException {
int filterMillis = 100;
DuplicateActionFilterByInsertTime<Runnable> expSet = new DuplicateActionFilterByInsertTime<>(filterMillis);
AtomicInteger purgeCount = new AtomicInteger(0);
expSet.addListener(new DuplicateActionFilterByInsertTime.Listener<Runnable>() {
#Override
public void onFilteringFinished(DuplicateActionFilterByInsertTime.FilteredItem<Runnable> filteredItem) {
purgeCount.incrementAndGet();
}
#Override
public void onFilteringStarted(Runnable runnable) {
}
});
Runnable key = () -> {
};
long beforeAddMillis = System.currentTimeMillis();
boolean added = expSet.run(key);
long afterAddMillis = System.currentTimeMillis();
Assert.assertTrue(added);
DuplicateActionFilterByInsertTime.SilenceInfo silenceInfo = expSet.get(key);
Assertions.assertThat(silenceInfo.getInsertTimeMillis()).isBetween(beforeAddMillis, afterAddMillis);
expSet.run(key);
DuplicateActionFilterByInsertTime.SilenceInfo silenceInfo2 = expSet.get(key);
Assert.assertEquals(silenceInfo.getInsertTimeMillis(), silenceInfo2.getInsertTimeMillis());
Assert.assertFalse(silenceInfo.getInsertTimeMillis() + filterMillis < System.currentTimeMillis());
Assert.assertEquals(silenceInfo.getCount(), 2);
Thread.sleep(filterMillis);
Assertions.assertThat(expSet.get(key)).isNull();
Assert.assertNull(expSet.get(key));
Thread.sleep(filterMillis * 2); // Give a chance to purge the items.
Assert.assertEquals(1, purgeCount.get());
System.out.println("Finished");
}
Source.

Ensuring thread safety for cache fetching values on its own

I built a generic cache that fetches values on miss by delegating to a ValueGenerator component. Internally, it has a map for values it has already obtained, and another map for in-flight requests so that they can be re-used across subscribers.
Here's the simplified code before I attempt to make it thread safe. My questions will follow.
public class NetworkCache<K, V> {
private final Map<K, V> mValues;
private final Map<K, Observable<V>> mRequests;
private final ValueGenerator<K, V> mValueGenerator;
public NetworkCache(ValueGenerator<K, V> valueGenerator) {
mValues = new HashMap<>();
mRequests = new HashMap<>();
mValueGenerator = valueGenerator;
}
public Observable<V> get(final K key) {
V value = mValues.get(key);
if (value != null) {
// We already had the value
return Observable.just(value);
}
Observable<V> requestObservable = mRequests.get(key);
if (requestObservable == null) {
// New request to fetch the value
requestObservable = mValueGenerator.generate(key);
// Store in-flight request for potential re-use
mRequests.put(key, requestObservable);
requestObservable.subscribe(new Subscriber<V>() {
#Override
public void onCompleted() { mRequests.remove(key); }
#Override
public void onError(Throwable e) { mRequests.remove(key); }
#Override
public void onNext(V value) { mValues.put(key, value); }
});
}
return requestObservable;
}
public interface ValueGenerator<K, V> {
Observable<V> generate(K key);
}
}
Now I'm trying to think how this could break under concurrency scenarios. I believe the focus should be on those two Map that are queried in get(), and modified in the subscribe callback.
I think it's reasonable to assume/enforce that this class can only be called on the main thread. The ValueGenerator, however, should be able to schedule its work on a different thread, as my use case is actually network requests.
I see 3 options, and I'd like help to figure out which one to use.
1. Use ConcurrentHashMap instead of HashMap
Constructor would change to:
public NetworkCache(ValueGenerator<K, V> valueGenerator) {
mValues = new ConcurrentHashMap<>();
mRequests = new ConcurrentHashMap<>();
mValueGenerator = valueGenerator;
}
With this approach, I don't know if it is sufficient and/or overkill.
2. Observe ValueGenerator call on main thread
To me, this means that all map operations would happen on the main thread (assuming that NetworkCache is only used there), even if the ValueGenerator used subscribeOn(Schedulers.io()). This would mean it is thread safe.
if (requestObservable == null) {
// New request to fetch the value
requestObservable = mValueGenerator
.generate(key)
.observeOn(AndroidSchedulers.mainThread());
...
3. Synchronize every access to the maps
I would keep using HashMap and the get method would become the following. Here, is synchronizing on the maps themselves the right approach? Do I need to block on every operation, or just put & remove?
public Observable<V> get(final K key) {
V value;
synchronized (mValues) {
value = mValues.get(key);
}
if (value != null) {
return Observable.just(value);
}
Observable<V> requestObservable;
synchronized (mRequests) {
requestObservable = mRequests.get(key);
}
if (requestObservable == null) {
requestObservable = mValueGenerator.generate(key);
synchronized (mRequests) {
mRequests.put(key, requestObservable);
}
requestObservable.subscribe(new Subscriber<V>() {
#Override
public void onCompleted() {
synchronized (mRequests) {
mRequests.remove(key);
}
}
#Override
public void onError(Throwable e) {
synchronized (mRequests) {
mRequests.remove(key);
}
}
#Override
public void onNext(V value) {
synchronized (mValues) {
mValues.put(key, value);
}
}
});
}
return requestObservable;
}
A little background on the utilization: the cache's get method would be called in rapid succession 1-10 times for different keys. That event would be infrequent, but could happen within a few seconds. It's when the second series of calls arrives, mixed with the observables from the first series coming back, that I worry about the execution.
I would do this with a single ConcurrentMap and AsyncSubject:
public class RequestCache<K, V> {
final ConcurrentMap<K, AsyncSubject<V>> values;
final Function<? super K, ? extends Observable<? extends V>> valueGenerator;
public RequestCache(
Function<? super K, ? extends Observable<? extends V>> valueGenerator) {
this.values = new ConcurrentHashMap<>();
this.valueGenerator = valueGenerator;
}
public Observable<V> get(K key) {
AsyncSubject<V> result = values.get(key);
if (result == null) {
result = AsyncSubject.create();
AsyncSubject<V> current = values.putIfAbsent(key, result);
if (current == null) {
Observable<? extends V> source = valueGenerator.apply(key);
source.subscribe(result);
} else {
result = current;
}
}
return result;
}
}
This is fully threadsafe and calls valueGenerator once per key only.
I think you should synchronize whole getter and setter functions.

ClassCastException: datastructures.instances.JClass cannot be cast to java.util.ArrayList

I know there are quite a few CCE questions on SO. I have read the majority of them either in detail or briefly and I cannot find anything that applies to my situation. My exact error is:
Exception in thread "pool-1-thread-1" java.lang.ClassCastException: datastructures.instances.JClass cannot be cast to java.util.ArrayList at if ((results = mCallsDownstreamCache.get(origin)) == null) {
As you will see in the code, what I'm doing is asking for an ArrayList from a cache (HashMap) and then making a decision on that. The odd behavior here is that datastructures.instances.JClass is in no way referenced in the piece of code that generates the error.
To give you some context, I have a database "model" which fulfills requests from a "controller". Those results are stored in a cache local to the model and if they exist the model will return the cache thus not having to hit the db. My caching elements are effectively decorators for the Commons' JCS.
The offending line is wrapped in a block comment and inline comment
public class AnalyzeModel extends Model {
public final String TAG = getClass().getSimpleName();
public CacheDecorator<Integer, JClass> mClassCache = new CacheDecorator<Integer, JClass>();
public CacheDecorator<Integer, JMethod> mMethodCache = new CacheDecorator<Integer, JMethod>();
public CacheDecorator<Integer, ArrayList<Integer>> mCallsUpstreamCache =
new CacheDecorator<Integer, ArrayList<Integer>>();
public CacheDecorator<Integer, ArrayList<Integer>> mCallsDownstreamCache =
new CacheDecorator<Integer, ArrayList<Integer>>();
public void close() {
super.close();
}
public Pair<Integer, ArrayList<Integer>> selectCallGraphDownstream(int origin) {
ArrayList<Integer> results = new ArrayList<Integer>();
/**
* This is the offending line
*/
if ((results = mCallsDownstreamCache.get(origin)) == null) {
// End error line
results = new ArrayList<Integer>();
for (Record r : mQuery.select(
mQuery.mQuerier.select(Calls.CALLS.TID)
.from(Calls.CALLS)
.where(Calls.CALLS.SID.eq(origin)))) {
results.add(r.getValue(Calls.CALLS.TID));
}
mCallsDownstreamCache.put(origin, results);
Statistics.CACHE_MISS++;
} else {
Statistics.CACHE_HITS++;
}
return new Pair<Integer, ArrayList<Integer>>(origin, results);
}
}
public class CacheDecorator<K, V> {
public final String TAG = getClass().getSimpleName();
private CacheAccess<K, V> mCache;
public CacheDecorator() {
try {
mCache = JCS.getInstance("default");
} catch (CacheException e) {
BillBoard.e(TAG, "Error getting cache configuration: " + e.toString());
e.printStackTrace();
}
}
/**
* Get an object from cache
* #param obj object to retrieve from cache
* #return generic object of retrieved value, null if not found
*/
public V get(K obj) {
return mCache.get(obj);
}
/**
* Place an object in cache
* #param key generic key for reference
* #param obj generic object to be cached
*/
public synchronized void put(K key, V obj) {
try {
if(obj != null) {
mCache.putSafe(key, obj);
}
} catch( CacheException e) {
//BillBoard.d(TAG, obj.toString());
//BillBoard.e(TAG, "Error placing item in cache: " + e.toString());
//e.printStackTrace();
}
}
/**
* Get the stats from our cache manager
* #return String of our cache object
*/
public String getStats() {
shutDownCache();
return mCache.getStats();
}
public static void shutDownCache() {
CompositeCacheManager.getInstance().shutDown();
}
}
Some additional details that may, or may not, be helpful:
The Pair<V, K> datastructure is just an immutable 2-pair tuple class
CacheDecorator.get(V obj) returns null if the object doesn't exist in cache
I've tried quite a bit in regards to casting and such
JClass does have references elsewhere in the code, but no reference in the offending method
JClass is a representation of a java class, it's a custom structure
By altering your config to include region-specific configs as presented in the documentation, and passing a region to your wrapper, should resolve the problem.
Looks like you are using the same cache region for all your wrappers and hence referencing the same 'under laying cache' across your wrappers. Could you change mCache = JCS.getInstance("default"); to something like mCache = JCS.getInstance("uniqueNameForWrapper"); for all your wrappers?

Dictionary-like data structure. Is this a good practice?

I need a data structure to store different type of objects.E.g. String, Boolean and other classes.
Is using a Map<String, Object> where using the key you get the according object which assumes that you know how to cast it a good practice?
Is there a better solution?
That's a perfect use case for a PropretyHolder I wrote a while ago. You can read in length about it on my blog. I developed it with immutability in mind, feel free to adapt it to your needs.
In general I'd say if you want to profit from type safety in Java you need to know your keys. What I mean by that - it will be hardly possible to develop type safe solution where keys come from external source.
Here's a special key that knows type of its value (it's not complete please download the source for complete version):
public class PropertyKey<T> {
private final Class<T> clazz;
private final String name;
public PropertyKey(Class<T> valueType, String name) {
this.clazz = valueType;
this.name = name;
}
public boolean checkType(Object value) {
if (null == value) {
return true;
}
return this.clazz.isAssignableFrom(value.getClass());
}
... rest of the class
}
Then you develop a data structure that utilizes it:
public class PropertyHolder {
private final ImmutableMap<PropertyKey<?>, ?> storage;
/**
* Returns value for the key of the type extending-the-one-declared-in-the {#link PropertyKey}.
*
* #param key {#link PropertyKey} instance.
* #return Value of the type declared in the key.
*/
#SuppressWarnings("unchecked")
public <T extends Serializable> T get(PropertyKey<T> key) {
return (T) storage.get(key);
}
/**
* Adds key/value pair to the state and returns new
* {#link PropertyHolder} with this state.
*
* #param key {#link PropertyKey} instance.
* #param value Value of type specified in {#link PropertyKey}.
* #return New {#link PropertyHolder} with updated state.
*/
public <T> PropertyHolder put(PropertyKey<T> key, T value) {
Preconditions.checkNotNull(key, "PropertyKey cannot be null");
Preconditions.checkNotNull(value, "Value for key %s is null",
key);
Preconditions.checkArgument(key.checkType(value),
"Property \"%s\" was given "
+ "value of a wrong type \"%s\"", key, value);
// Creates ImmutableMap.Builder with new key/value pair.
return new PropertyHolder(filterOutKey(key)
.put(key, value).build());
}
/**
* Returns {#link Builder} with all the elements from the state except for the given ket.
*
* #param key The key to remove.
* #return {#link Builder} for further processing.
*/
private <T> Builder<PropertyKey<? extends Serializable>, Serializable> filterOutKey(PropertyKey<T> key) {
Builder<PropertyKey<? extends Serializable>, Serializable> builder = ImmutableMap
.<PropertyKey<? extends Serializable>, Serializable> builder();
for (Entry<PropertyKey<? extends Serializable>, Serializable> entry : this.storage.entrySet()) {
if (!entry.getKey().equals(key)) {
builder.put(entry);
}
}
return builder;
}
... rest of the class
}
I omit here a lot of unnecessary details please let me know if something is not clear.
A typesafe heterogeneous container can be used for this purpose:
import java.util.HashMap;
import java.util.Map;
public class Container {
private Map<Class<?>, Object> container = new HashMap<Class<?>, Object>();
public <T> void putElement(Class<T> type, T instance) {
if (type == null) {
throw new NullPointerException("Type is null");
}
//container.put(type, instance); // 'v1'
container.put(type, type.cast(instance)); // 'v2' runtime type safety!
}
public <T> T getElement(Class<T> type) {
return type.cast(container.get(type));
}
public static void main(String[] args) {
Container myCont = new Container();
myCont.putElement(String.class, "aaa");
myCont.putElement(Boolean.class, true);
myCont.putElement(String[].class, new String[] {"one", "two"});
System.out.println(myCont.getElement(String.class));
System.out.println(myCont.getElement(String[].class)[1]);
}
}
Limitation: this container in its form is capable only to store one instance/object type.
In putElement() you can achieve runtime type safety by using a dynamic cast. This will hoewever add an extra overhead.
E.g: Try to pass a raw class object to the container. Note where the exception occurs:
Class raw = Class.forName("MyClass");
myCont.putElement(raw, "aaa"); //ClassCastException if using 'v2'
System.out.println(myCont.getElement(raw)); //ClassCastException if using 'v1'

Categories