java - concurrent updates to multiple objects - java

I am new in Java concurrency, I have a class holding data (doubles in the sample code below) that should be accessed with a get (like a Map), but with data stored internally in a array for performance reasons.
This run in a multithreaded environment and this index must be updated sometimes.
public class ConcurrencySampleCode {
private static Object lock = new Object();
private Map<String, Integer> map = ...
private double[] array = ...
public Double get(String id) {
synchronized (lock) {
Integer i = map.get(id);
if (i == null) {
return null;
}
return array[i];
}
}
public void update() {
Map<String, Integer> tmpMap = updateMap(...);
double[] tmpArray = updateArray(...);
synchronized (lock) { // should be atomic
map = tmpMap;
array = tmpArray;
}
}
}
I am not sure whether this code is correct or not? Also, is the synchronized keyword needed in the get function ?
Is there a better way of doing this ?
Thanks for your help

There's nothing wrong with your code, but you will need to use the volatile keyword on the map and the array to ensure all threads see the updated values immediately, and I'm not sure you want the lock to be static.
As an alternative you may want to check out the java.util.concurrent.atomic package. It has some handy thread-safe variable. For example you could move your map and array into their own class, then use the AtomicReference to store the object.
public class ConcurrencySampleCode {
private AtomicReference<DoubleMap> atomicMap = new AtomicReference(new DoubleMap());
//Inner class used to hold the map and array pair
public class DoubleMap {
private Map<String, Integer> map = ...
private double[] array = ...
}
public Double get(String id) {
DoubleMap map = atomicMap.get();
...
}
public void update() {
Map<String, Integer> tmpMap = updateMap(...);
double[] tmpArray = updateArray(...);
DoubleMap newMap = new DoubleMap(tmpMap, tmpArray);
atomicMap.set(newMap);
}
}

There is a lot going on in concurrent programming, but for instance your update() method is faulty. In the current state multiple Threads can call ConcurrencySampleCode.update() and every each one of them will initiate both update calls inside the body before the synchronization kicks in. This means that after the round-robin turnover the last Thread with the update call will not have the changes from the previous update calls in the newly update map and array.
Long story, try to use and understand the ConcurrentHashMap

Related

How to implement thread-safe HashMap lazy initialization when getting value in Java?

I want to implement a util getting an Enum object by its string value. Here is my implementation.
IStringEnum.java
public interface IStringEnum {
String getValue();
}
StringEnumUtil.java
public class StringEnumUtil {
private volatile static Map<String, Map<String, Enum>> stringEnumMap = new HashMap<>();
private StringEnumUtil() {}
public static <T extends Enum<T>> Enum fromString(Class<T> enumClass, String symbol) {
final String enumClassName = enumClass.getName();
if (!stringEnumMap.containsKey(enumClassName)) {
synchronized (enumClass) {
if (!stringEnumMap.containsKey(enumClassName)) {
System.out.println("aaa:" + stringEnumMap.get(enumClassName));
Map<String, Enum> innerMap = new HashMap<>();
EnumSet<T> set = EnumSet.allOf(enumClass);
for (Enum e: set) {
if (e instanceof IStringEnum) {
innerMap.put(((IStringEnum) e).getValue(), e);
}
}
stringEnumMap.put(enumClassName, innerMap);
}
}
}
return stringEnumMap.get(enumClassName).get(symbol);
}
}
I wrote a unit test in order to test whether it works in multi-thread case.
StringEnumUtilTest.java
public class StringEnumUtilTest {
enum TestEnum implements IStringEnum {
ONE("one");
TestEnum(String value) {
this.value = value;
}
#Override
public String getValue() {
return this.value;
}
private String value;
}
#Test
public void testFromStringMultiThreadShouldOk() {
final int numThread = 100;
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch doneLatch = new CountDownLatch(numThread);
List<Boolean> resultList = new LinkedList<>();
for (int i = 0; i < numThread; ++i) {
new Thread(() -> {
try {
startLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
resultList.add(StringEnumUtil.fromString(TestEnum.class, "one") != null);
doneLatch.countDown();
}).start();
}
startLatch.countDown();
try {
doneLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(numThread, resultList.stream().filter(item -> item.booleanValue()).count());
}
}
The testing result is:
aaa:null
java.lang.AssertionError:
Expected :100
Actual :98
It denotes that only one thread execute this line of code:
System.out.println("aaa:" + stringEnumMap.get(enumClassName));
So the initialization codes should be executed by only one thread.
The strange thing is, the result of some thread will be null after executing this line of code:
return stringEnumMap.get(enumClassName).get(symbol);
Since there is no NullPointerException, stringEnumMap.get(enumClassName) must return the reference of innerMap. But why it will get null after calling get(symbol) of innerMap?
Please help, it drive me crazy the whole day!
The problem is due to the line
List<Boolean> resultList = new LinkedList<>();
From JavaDoc of LinkedList:
Note that this implementation is not synchronized.If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list.If no such object exists, the list should be "wrapped" using the Collections.synchronizedListmethod. This is best done at creation time, to prevent accidental unsynchronized access to the list:
List list = Collections.synchronizedList(new LinkedList(...));
As LinkedList is not thread safe, and unexpected behavior may happens during the add operation.
Which cause the resultList size less than the thread count, and hence the expected count is less than the result count.
To get correct result, add Collections.synchronizedList as suggested.
Although you implementation is fine, I suggest you to follow Matt Timmermans answer for simpler and robust solution.
stringEnumMap should be a ConcurrentHashMap<String, Map<String,Enum>>, and use computeIfAbsent to do the lazy initialization.
ConcurrentMap interface
As others noted, if manipulating a Map across threads you must account for concurrency.
You could handle concurrent access yourself. But there is no need. Java comes with two implementations of Map that are built to internally handle concurrency. These implementations implement the ConcurrentMap interface.
ConcurrentSkipListMap
ConcurrentHashMap
The first maintains the keys in sorted order, implementing the NavigableMap interface.
Here is a table I authored to show the characteristics of all the implementations of Map bundled with Java 11.
You might find other third-party implementations of the ConcurrentMap interface.
try moving
if (!stringEnumMap.containsKey(enumClassName))
and the
return stringEnumMap.get(enumClassName).get(symbol);
into the synchronized block.

synchronize a method by achieving better performance?

I have a class that is being called by multiple threads on multi core machine. I want to make it thread safe.
add method will be called by multiple threads. And if key exists, just append the current value to new value otherwise just put key and value in the map.
Now to make it thread safe, I was planning to synchronize add method but it will destroy performance. Is there any better way by which we can achieve better performance without synchronizing add method?
class Test {
private final Map<Integer, Integer> map = new ConcurrentHashMap<>();
public void add(int key, int value) {
if (map.containsKey(key)) {
int val = map.get(key);
map.put(key, val + value);
return;
}
map.put(key, value);
}
public Object getResult() {
return map.toString();
}
}
but it will destroy performance
It likely wouldn't destroy performance. It will reduce it some, with further reduction if there is a high collision rate.
Is there any better way by which we can achieve better performance?
Yes, use merge() (Java 8+). Quoting the javadoc:
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null.
Example:
public void add(int key, int value) {
map.merge(key, value, (a, b) -> a + b);
}
Or using a method reference to sum(int a, int b) instead of a lambda expression:
public void add(int key, int value) {
map.merge(key, value, Integer::sum);
}
Use merge:
class Test {
final Map<Integer, Integer> map = new ConcurrentHashMap<>();
public void add(int key, int value) {
map.merge(key, value, Integer::sum);
}
public Object getResult() {
return map.toString();
}
}
Java 7 solution if you absolutely can't use synchronized (or, you absolutely cannot lock explicitly):
class Test {
final Map<Integer, AtomicInteger> map = new ConcurrentHashMap<>();
public void add(int key, int value) {
get(key).addAndGet(value);
}
private AtomicInteger get(int key) {
AtomicInteger current = map.get(key);
if (current == null) {
AtomicInteger ai = new AtomicInteger();
current = map.putIfAbsent(key, ai);
if (current == null) {
current = ai;
}
}
return current;
}
public Object getResult() {
return map.toString();
}
}
synchronized causes a bottleneck only when you run an expensive operation holding a lock.
In your case by adding a synchronized you are doing:
1. check a hashmap for existence of a key
2. get the value mapped to that key
3. do an addition and put the result back to the hashmap.
All these operations are super cheap O(1) and unless you are using some strange pattern for the keys which are integers it should be very unlikely that you can get some degenerate performance due to collisions.
I would suggest if you can't use merge as the other answers point out, to just synchronize. You should be considered so much about performance only in critical hotpaths and after you have actually profiled that there is an issue there

Return all instance one variables in one call

I have this class that serves as a container which I will use the instance variable for processing later
class Data{
static int counter= 0;
boolean boolean1;
String string1;
public Data() {
counter++;
}
}
And I have this method that sets the values of Data
public Data setData()
{
Data data = null;
for (int i = 0; i < somecoutnerhere; i++) {
Data = new Data();
Data.boolean1 = some boolean put here;
Data.string1 = "some string to be put here";
}
return ProcessData(Data);
}
I also have this class ProcessData that will make use of Data and will construct the response
private class ProcessData
{
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data)
{
map.put("boolean1", data.boolean1);
map.put("String1", data.string1);
counter = data.counter;
}
public String someMethodToGenerateReturnData(){
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
According to this your problem is not "returning all instance one variables in one call", as your title states, but rather a question about how returning all Data-Objects created in your for-loop, which is easier.
Your code is erronous though, so I went ahead & corrected it (I hope I didn't mess up). I also renamed a few things.
The changes I made are:
renamed "boolean1" and "string1" to "trueOrFalse" and "string"
added a public, fully parameterized constructor to the Data-class
added a ProcessData-list to the setData()-method, which is filled in the for-loop
(+ a comment)
However, I'd strongly recommend you to check your architecture, and also to learn a bit about naming conventions, or coding conventions in general. Names should point out the purpose or content of the method/variable/class, and "boolean1" isn't really doing that.
Regarding the architecture: The Data-class seems to exist solely for the counter, and you could easily change that, making the Data-class obsolete (unless it's used somewhere else).
Data class:
class Data {
static int counter = 0;
boolean trueOrFalse;
String string;
public Data() {
counter++;
}
public Data(boolean someBoolean, String someString) {
this.trueOrFalse= someBoolean;
this.string = someString;
counter++;
}
}
setData()-Method:
public List<ProcessData> setData() {
List<ProcessData> processedDataList = new ArrayList<ProcessData>();
for (int i = 0; i < someCounterHere; i++) {
processedDataList.add(new ProcessData(new Data(true, "testString"));
// a new Data-object is created (parameters true and "testString")
// a new ProcessData-object is created (parameter is the newly created Data-Object)
// the newly created ProcessData-object is added to the list
}
return processedDataList;
}
ProcessData-class:
private class ProcessData {
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data) {
map.put("trueOrFalse", data.trueOrFalse);
map.put("string", data.string);
counter = data.counter;
}
public String someMethodToGenerateReturnData() {
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}

Java thread safe DAO

I have a DAOClass which is called from many Threads as below for inserting into a set of tables -
public class DAOClass
{
private HashMap<String, HelperClass> insertBuffer;
public DAOClass()
{
insertBuffer = new HashMap<String, HelperClass>();
}
public int[] createSomeTable(String key, SomeTableRecord someTableRecord)
{
List<SomeTableRecord> someTableRecList;
HelperClass buf = insertBuffer.get(key);
if (buf == null)
{
buf = new HelperClass();
insertBuffer.put(key, buf);
}
someTableRecList = buf.getSomeTableBuffer();
someTableRecList.add(someTableRecord);
if(someTableRecList.size() >= Global.limit())
{
return flushSomeTableInsertCache(key);
}
else
{
return null;
}
}
public int[] flushSomeTableInsertCache(String key)
{
HelperClass buf = insertBuffer.get(key);
int[] retVal = null;
if (buf != null && buf.getSomeTableBuffer() != null)
{
retVal = createSomeTableBuffered(buf.getSomeTableBuffer());
buf.getSomeTableBuffer().clear();
}
return retVal;
}
}
public int[] createSomeTableBuffered(final List<SomeTableRecord> someTableRecordList)
{
INSERT QUERY GOES HERE from LIST..
}
}
Different Threads call createSomeTable method which adds to an ArrayList of a HelperClass. There is a HashMap but the key is overlapping i.e same key is hit by multiple threads simultaneously, thus corrupting HashMap and untimely flushings ..
Helper Class follows -
class HelperClass {
private String key;
private ArrayList<SomeTableRecord> someTableBuffer;
private ArrayList<SomeTable1Record> someTable1Buffer;
HelperClass() {
someTableBuffer = new ArrayList<SomeTableRecord>();
someTable1Buffer = new ArrayList<SomeTable1Record>();
}
public ArrayList<SomeTableRecord> getSomeTableBuffer() {
return someTableBuffer;
}
public ArrayList<SomeTable1Record> getSomeTable1Buffer() {
return someTable1Buffer;
}
}
But, this is apparently not thread safe as key is not disjoint. Can you please suggest some correction in the classes so that it is thread safe.
You should rather use ArrayList<HelperClass> than HashMap. To avoid conflicts, use
public synchronized int[] createSomeTable(String key, SomeTableRecord someTableRecord)
to protect your buffer.
UPDATE:
To protect the buffer even in Spring, add synchronized to flushSomeTableInsertCache as well:
public synchronized int[] flushSomeTableInsertCache(String key)
Actually you don't use keys just to identify the elements.
Otherwise it is not a good strategy to watch key collisions this way, because they can even happen between 2 flushes, so you should either check them in the database, or have a separate HashSet for the keys (if you are sure that you have all the keys in there).
Use class ConcurrentHashMap instead.
insertBuffer is the only state here. Modifying its content in a multi-threaded environment might result in unexpected behavior. You can either synchronize access to it or use ConcurrentHashMap instead of HashMap.
I would use synchronized methods rather than ConcurrentHashMap. However, using ConcurrentHashMap might solve your thread-safe-issue as well.
The simplest way to separate the usage is to create one DAOClass object for each thread.
Change your implementation to ConcurrentHashMap, that will solve your concurrency issue.

How to generate a unique hash code for a method instance?

I am doing some profiling with Aspectj.
I need to identify uniquely the instances of a method where the field been accessed
For example:
public class Class{ int a; int b;
public void method1(){
setA(5);
setB(6);
In this case with AspectJ I can obtain that an access to a and an access to b have been made by setA and SetB methods. And with
Thread.currentThread().getStackTrace();
I can know that setA and setB have been called by method1().
The name of the method is not enough I need also to univocally identify the instance of the method.
For example if method1 is called many times I have to discern that the access to a and access to b have been made by different instances of method1.
Any suggestion how can obtain the instance hashcode of a method excution?
A simple (untested, use at your own risk) solution that could possibly work would be to maintain a counter per method per thread:
private static final ConcurrentHashMap<String, ConcurrentHashMap<Long, AtomicInteger>>
COUNTERS = new ConcurrentHashMap<>();
public static int getInvocationId(String methodName, long threadId) {
return counter(methodName, threadId).getAndIncrement();
}
private static AtomicInteger counter(String methodName, long threadId) {
ConcurrentHashMap<Long, AtomicInteger> map = countersForMethodName(methodName);
AtomicInteger counter = map.get(threadId);
if (counter == null) {
AtomicInteger newCounter = new AtomicInteger();
counter = map.putIfAbsent(threadId, newCounter);
if (counter == null) {
return newCounter;
}
}
return counter;
}
private static ConcurrentHashMap<Long, AtomicInteger> countersForMethodName(
String methodName) {
ConcurrentHashMap<Long, AtomicInteger> map = COUNTERS.get(methodName);
if (map == null) {
ConcurrentHashMap<Long, AtomicInteger> newMap = new ConcurrentHashMap<>();
map = COUNTERS.putIfAbsent(methodName, newMap);
if (map == null) {
return newMap;
}
}
return map;
}
Then, in your advice, something like:
int invocationId = getInvocationId(thisJoinPoint.getSignature().getName(),
Thread.currentThread().getId());
// do what you want with invocationId
Note that this relies on the advice executing in the same thread as the target method—unfortunately, I'm not that familiar enough with AspectJ to know whether this assumption will always hold true.
CAVEAT: If your environment creates and expires new threads all the time, then the above tree will keep growing (essentially, a memory leak). If this is a problem, then you'll need to put in some other code to periodically enumerate all active threads, and prune the expired entries from the tree. In that case, you might want to use a map per-thread id, then per-method name to make pruning more efficient.

Categories

Resources