Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java synchronization depending on method parameter

how can I provide synchronization upon method parameter values?

All method calls using the 'same' parameter value A should be synchronized. A method call with a different parameter value e.g. B can access, even when calls with A are already waiting. The next concurrent call for B must wait also for the first B to be released.

My use case: I want to synchronize the access to JPA entities on ID level but want to avoid pessimistic locking because I need kind of a queue. The 'key' for locking is intended to be the entity ID - which is in fact of the type Java Long.

protected void entityLockedAccess(SomeEntity myEntity) {
    //getId() returns different Long objects so the lock does not work
    synchronized (myEntity.getId()) {
        //the critical section ...
    }
}

I read about lock objects but I am not sure how they would suit in my case. On the top level I want to manage a specific REST call to my application which executes critical code.

Thanks, Chris

like image 440
theFriedC Avatar asked Jul 09 '18 08:07

theFriedC


People also ask

Can we use synchronized for method in Java?

Java Synchronized MethodSynchronized method is used to lock an object for any shared resource. When a thread invokes a synchronized method, it automatically acquires the lock for that object and releases it when the thread completes its task.

Is synchronized part of method signature?

synchronized is not part of the method signature.

Can you synchronize run method?

Yes, we can synchronize a run() method in Java, but it is not required because this method has been executed by a single thread only. Hence synchronization is not needed for the run() method.


2 Answers

As far as I understood you basically want a different, unique lock for each of your SomeEntity IDs.

You could realize this with a Map<Integer, Object>.

You simply map each ID to an object. Should there already be an object, you reuse it. This could look something like this:

static Map<Integer, Object> locks = new ConcurrentHashMap<>();

public static void main(String[] args)
{
    int i1 = 1;
    int i2 = 2;

    foo(i1);
    foo(i1);
    foo(i2);
}

public static void foo(int o)
{
    synchronized (locks.computeIfAbsent(o, k -> new Object()))
    {
        // computation
    }
}

This will create 2 lock objects in the map as the object for i1 is reused in the second foo(i1) call.

like image 184
Ben Avatar answered Sep 30 '22 03:09

Ben


Objects which are pooled and potentially reused should not be used for synchronization. If they are, it can cause unrelated threads to deadlock with unhelpful stacktraces.

Specifically, String literals, and boxed primitives such as Integers should NOT be used as lock objects because they are pooled and reused.

The story is even worse for Boolean objects because there are only two instances of Boolean, Boolean.TRUE and Boolean.FALSE and every class that uses a Boolean will be referring to one of the two.

I read about lock objects but I am not sure how they would suit in my case. On the top level I want to manage a specific REST call to my application which executes critical code.

You DB will take care for concurrent writes and other transactional issues. All you need to do is use Transactions.

I would also recommend you to go through the classical problems (DIRTY READs NON Repeatable reads). You can also use Optimistic Locking for

like image 42
Yati Sawhney Avatar answered Sep 30 '22 03:09

Yati Sawhney