Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Concurrency control multiple locks

I have a critical section, I need to control that only those threads with a given attribute value are able to enter at the same time.

For example: I have Thread#1 that handles products, Thread#2 handles products too and Thread#3 handles services

now:

  1. T1 comes first and enters the critical section, so that the product is handled

  2. T2 wants to enter, but since there is another product being processed, it must wait

  3. T3 comes last, it is able to enter, since it needs a service (not a product) to be processed

  4. T1 goes out, now T2 is able to get in

I think it looks quite simple, but I wasn't able to find anything that fits the requirement. My question is, how can I achieve this? any reference to any source of information about this will be appreciated

Thank you very much in advance

like image 869
McCoy Avatar asked Aug 22 '16 17:08

McCoy


3 Answers

How about this:

private ConcurrentMap<Integer, Semaphore> semaphores = new ConcurrentHashMap<>();

public void enter(int id) {
    Semaphore s = semaphores.computeIfAbsent(id, key -> new Semaphore(1));

    try {
        s.acquire();
        // Critical section.
    } catch (InterruptedException e) {
        // Exception handling.
    } finally {
        s.release();
    }
}
  • The hash map guarantees fast access and can grow dynamically. Furthermore, you're able to use objects as keys.
  • If you know the number of IDs at compile time, you can also use an unmodifiable or immutable map and prefill it.
  • The semaphore can be adjusted as well: set different numbers of permits and fairness guarantees for different IDs.
  • The wrapping class can provide additional methods, e.g. a non-blocking tryEnter(int) via Semaphore#tryAcquire().
like image 194
beatngu13 Avatar answered Oct 05 '22 22:10

beatngu13


I think you cannot lock on value types but try this workaround: Put lock objects in an array and access the array from ID.

Something like:

private Object[] locks = new Object[] {new Object(), new Object()};

private void yourMethod(int id)
{
    synchronized(locks[id]) //or id - 1 if you want to stick to your 1 and 2
    {
        //do your things
    }
}

Hope it helps

like image 32
Winter Avatar answered Oct 06 '22 00:10

Winter


If you really need this logic, it can be implemented this way:

private final Lock lock = new Lock();

public void process() {
    boolean locked = false;
    if (isProduct()) {   // Depends on the current thread
        lock.lock();
        locked = true;
    }

    try {
        // Do the work here
    } finally {
        if (locked) {
            lock.unlock();
        }
    }
}

But the question itself tells about a poor design. Just create methods processProduct() and processService(), make the first one synchronized and call the one you really need from your threads.

like image 21
Andrew Lygin Avatar answered Oct 05 '22 23:10

Andrew Lygin