简体   繁体   中英

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

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

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, eg a non-blocking tryEnter(int) via Semaphore#tryAcquire() .

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.

We sorted this issue out differently, with a high performance impact, using an ordinary lock which will be a bottleneck under some scenarios. But I managed to get a solution, not tested though. This consists of a Lock class which contains a pool of Locks and provides them to threads according a termId. All threads who share the same termId will be provided with the same Lock. This way we'll get all threads grouped by termId, all lined up in different queues. I set a fixed pool of locks, though I think it could be dynamic also,removing and adding Locks on demand.

Any feedback will be appreciated

import java.util.ArrayList;
import java.util.concurrent.Semaphore;

public class Lock {

  private Lock() {}

  private static class SingletonHolder {

    /** ordered list. Locks with termId null must be at the end **/
    public static final ArrayList<Lock> INSTANCES = new ArrayList<Lock>(){{
      add(new Lock());
      add(new Lock());
      add(new Lock());
      add(new Lock());
      add(new Lock());
      add(new Lock());
      add(new Lock());
    }};

    public static synchronized Lock returnInstance(String termId){
      ArrayList<Lock> sortedList = sortInstances();
      for(Lock instance: sortedList){
        if(termId.equals(instance.getTermId()))
          return instance;
        if(instance.getTermId()==null)
          return instance;
      }

      return null;
    }

    public static ArrayList<Lock> sortInstances(){
      ArrayList<Lock> sortedList=new ArrayList<Lock>();
      for(Lock instance: INSTANCES){
        if(instance.getTermId()==null)
          sortedList.add(instance);
        else
          sortedList.add(0,instance);
      }

      return sortedList;
    }
  }

  /**
  * Use this method to get a reference to a singleton instance of
  * {@link Lock}
  * 
  * @return a singleton instance
  */
  public static Lock getInstance(String termId) {
    return SingletonHolder.returnInstance(termId);
  }

  private Semaphore lock = new Semaphore(1);
  private String termId;

 public void getLock(String termId) throws InterruptedException {
   lock.acquire();
   setTermId(termId);
 }

 public void releaseLock() {   
   lock.release();
   setTermId(null);
 }

  public String getTermId() {
    return termId;
  }

  public void setTermId(String termId) {
    this.termId = termId;
  }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM