Java并发控制多个锁

Java Concurrency control multiple locks

我有一个临界区,我需要控制只有具有给定属性值的线程才能同时进入。

例如:我有线程#1 处理产品,线程#2 也处理产品,线程#3 处理服务

现在:

  1. T1先到,进入临界区,这样乘积就处理好了

  2. T2想进入,但是因为有其他产品正在处理,所以必须等待

  3. T3排在最后,可以进入,因为它需要处理一个服务(不是产品)

  4. T1出去了,现在T2可以进来了

我觉得它看起来很简单,但我找不到符合要求的东西。 我的问题是,我怎样才能做到这一点?任何对此的任何信息来源的引用将不胜感激

非常感谢您

我认为您不能锁定值类型,但请尝试以下解决方法:将锁定对象放入数组中并从 ID 访问该数组。

类似于:

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
    }
}

希望对您有所帮助

如果你真的需要这个逻辑,可以这样实现:

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();
        }
    }
}

但是这个问题本身说明了一个糟糕的设计。只需创建方法 processProduct()processService(),创建第一个 synchronized 并从您的线程中调用您真正需要的方法。

我们对这个问题进行了不同的排序,具有高性能影响,使用普通锁,这在某些情况下会成为瓶颈。但我设法得到了一个解决方案,虽然没有经过测试。这由一个 Lock class 组成,它包含一个 Locks 池并根据 termId 将它们提供给线程。共享相同 termId 的所有线程将被提供相同的锁。这样我们就能得到所有按 termId 分组的线程,所有线程都排在不同的队列中。我设置了一个固定的锁池,虽然我认为它也可以是动态的,按需删除和添加锁。

如有任何反馈,我们将不胜感激

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;
  }
} 

这个怎么样:

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();
    }
}
  • 散列映射保证快速访问并且可以动态增长。此外,您还可以将对象用作键。
  • 如果你在编译时知道ID的数量,你也可以使用不可修改或不可变的映射并预填充它。
  • 信号量也可以调整:为不同的ID设置不同的许可数和公平性保证。
  • 包装 class 可以提供额外的方法,例如通过 Semaphore#tryAcquire().
  • 的非阻塞 tryEnter(int)