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.
Related
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
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.
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
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
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.