Java实现选择性同步



我在一个应用程序中遇到了一个问题,其中两个具有相同参数的线程运行,发生了冲突。一种解决方案是使用同步块,但由于该问题只发生在具有相同参数的线程上,因此效率非常低。我想到的一种方法是使用并发映射将参数组合存储为键,将对象存储为值,每当线程开始操作时,它首先检查映射是否包含键(参数组合),如果包含,它将等待存储该组合的对象。线程最后将从映射中删除该对象,并对其调用notify。这种方法的问题是为相同的参数组合生成相同的对象。例如:如果thread1插入到map中,并调用notify并删除它,thread2可能会从等待中出来,但其他线程永远不会出来,因为对象从map中丢失了。

是否有另一种优雅的方法来解决这个问题?

创建参数组合池。每次需要保证一个线程正在运行时,调用Pool.acquire(params)

像这样使用:

用良好的hashCodeequals包裹对象中的参数。

在线程:

synchronized(pool) {
  pool.acquire(myParams);
}
// do the work
synchronized(pool) {
  pool.release(myParams);
}

池:

class Pool {
  Set<Params> lockedParams;
  // In Pool - note that this is synchronized!
  void acquire(Params params) {
    Params lock = lockedParams.get(params);
    if(lock != null) {
      // Locked by another thread
      lock.wait();
    }
    lockedParams.add(params);
  }
  void release(Params params) {
    Params lock = lockedParams.remove(params);
    lock.notifyAll();
  }
}

这是一个想法。您需要为现实生活中的情况添加一些错误处理(finally中的release(),在本例中Pool.release()中的lock处理null,等等)。

调用notifyAll而不是notify,这样所有等待的线程都将被通知。为什么要从map中移除同步对象?尝试获取对象上的锁,如果线程成功,则进入临界区,否则开始等待。

您可以为等待添加超时,因此,如果线程在一段时间内无法获得锁,则它失败。这样你的代码就不会陷入死锁。

如果您可以将参数(或它们的子集)表示为字符串,则可以在interned字符串上同步

最新更新