0

In our web application exists 'Access Bean' classes with some methods on them which perform queries on a DB, these are invoked multiple times per request. Therefore they need to be cached but the original classes cannot be altered as they are from the framework we use, so to work around this, I have created a factory which builds a proxy class to wrap and cache method calls to original existing bean methods. I want to store the proxy class in a ConcurrentHashMap<Class, Class> (where the key is the original class and the value is the new proxy class) to be quickly retrieved by other threads when needed.

I want it to be fast and with very low contention, so I have to synchronized on the bean class object passed in as a method parameter for double checked locking to get the proxy class from the map or controlling access to the proxy factory if the proxy class is missing.

Map<Class, Class> classProxyMap = ConcurrentHashMap<Class, Class>;

@SuppressWarnings("unchecked")
private static <P extends B> Class<P> getProxyClass(Class<B> beanClass) {

Class<P> proxyClass = classProxyMap.get(beanClass);

    if (proxyClass == null) {
        synchronized (beanClass) {
            proxyClass = classProxyMap.get(beanClass);
            if (proxyClass == null) {
                proxyClass = proxyFactory.makeProxyClass(beanClass, false);
                classProxyMap.put(beanClass, proxyClass);
            }
        }
    }

    return (Class<P>) proxyClass;
}

It seems as though this is failing in a QA environment, although I am not entirely sure this (synchronization) is the issue. Therefore I want to know if it is ok to synchronize in this way or should I just synchonize on the map and be done with it.

[EDIT] The Application server is Java 7 so ConcurrentHashMap.computeIfAbsent is not an option

7
  • 4
    Synchronizing on external things is dangerous, especially since the Class is used as lock for static synchronized methods in that class. I also doubt you'll gain a lot by adding this synchronized block because if there happens to be another thread that creates a proxy in the short time it takes to create one you'd probably live without issues. You could use ConcurrentMap#putIfAbsent in Java7 without extra locks if the risk of a few duplicate proxies is ok. Otherwise just synchronize on something you "own". Commented Jan 21, 2019 at 22:30
  • AFAIK There should only ever be one instance of Class<ExistingBeanClass> (like enum) so I believe this should be safe. Is there any chance that synchonizing like this on the class itself could fail? Please give an example if you can. I do realize it might not be worth the speed up. Commented Jan 21, 2019 at 22:38
  • 1
    @lifesoordinary "Synchronizing on external things is dangerous" the point is that the same instance of Class<ExistingBeanClass> can be obtained and synchronized on elsewhere in your code, which could lead to contention here. Commented Jan 21, 2019 at 23:02
  • Double-checked locking is flawed, unnecessary, and if done wrong, ineffective. Yours is done wrong because the first null check on the lock object is unguarded by any thread coordination, so race-condition risk is rampant. Commented Jan 22, 2019 at 3:28
  • @LewBloch this should actually be ok here since proxyClass comes from a ConcurrentHashMap which does all the thread coordination & visibility guarantees you'd typically use a volatile for. Commented Jan 22, 2019 at 3:47

1 Answer 1

0

What for do you need extra synchronization if you already have ConcurrentHashMap?

Why not to use existing ConcurrentHashMap#computeIfAbsent?

Map<Class, Class> classProxyMap = ConcurrentHashMap<Class, Class>;

@SuppressWarnings("unchecked")
private static <P extends B> Class<P> getProxyClass(Class<B> beanClass) 
{
    return classProxyMap.computeIfAbsent(
            beanClass, 
            (clazz) -> proxyFactory.makeProxyClass(clazz, false));
}
Sign up to request clarification or add additional context in comments.

1 Comment

The Web app server is java 7 only so compute if absent does not exist

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.