我可以按参数同步方法吗?



>我可以按参数同步方法吗?

例如 - 我让人到某个方法,我想为人做一些操作,但如果很少有线程为同一个人调用此方法,我想一个接一个地做。

private void dosomething(Long id, Person person) {
dosomethingelse(id, person);
}

如何只为同一个id一一调用doelse(id,person(? 但我希望不同 id-s 的这段代码可以多线程调用

我写了这段代码,但也许这里有点问题,或者有什么可以更好的。

public static class LatchByValue <T> {
public void latch(T value, ConsumerWithException<T> consummer) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try {
CountDownLatch previousLatch = null;
// we are checking if another thread is already calling this method with the same id
// if sync has CountDownLatch so another thread is already calling this method 
// or we put our latch and go on
while ((previousLatch = sync.putIfAbsent(value, latch)) != null) {
try {
// we are waiting for another thread, we are waiting for all threads that put their latch before our thread
previousLatch.await();
} catch (InterruptedException e) {
return;
}
}
consummer.accept(value);
} finally {
latch.countDown();
sync.remove(value, latch);
} 
}
private ConcurrentHashMap<T, CountDownLatch> sync = new ConcurrentHashMap<>();
}

例:

LatchByValue<Long> latch = new LatchByValue<>();
private void dosomething(Long id, Person person) {
latch.latch(
id,
currentId -> { dosomethingelse(currentId, person); }
);
}

使用CountdownLatch的问题在于您无法"增加"计数,因此您需要在使用现有闩锁时更换它,这使代码复杂化。

您可以改用一个许可证Semaphore,这将允许您以更简单的方式做同样的事情。

Semaphore s = sync.computeIfAbsent(value, x -> new Semaphore(1, true));
s.acquire(); //this blocks and throws InterruptedException, which you need to handle
try {
consummer.accept(value);
} finally {
s.release();
}

您可以在传递的参数上使用synchronized关键字(罪魁祸首:它不能为空!这也使您不再担心重新获取锁(它是可重入的(。

所以实现看起来像:

private void doSomething(Long id, Person person) {
synchronized (person) {
// do something
}
}

请记住,任何其他访问(不在doSomething调用中(也需要具有同步块,例如:

// another method, unrelated, but does something with 'person'
private void doSomethingElse(Person person, ... /* other arguments */) {
synchronized (person) {
// do something
}
}

用户需要获取该对象的锁将是很好的文档(在Person的javadoc中(。


如果要为元组提供关键部分<id, person>则需要稍微更改 API - 然后在应用程序中传递该对象。

private void doSomething(IdAndPerson idAndPerson) {
synchronized (idAndPerson) {
// do something
}
}
class IdAndPerson {
private final Long id;
private final Person person;
// constructor etc.
}
private static final Set<Long> lockedIds = new HashSet<>();
private void lock(Long id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Long id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void doSomething(Long id) throws InterruptedException {
try {
lock(id);
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(id);
}
}
  • id不仅可以是"Long",还可以是具有正确覆盖的"equals"和"hashCode"方法的任何类。
  • try-finally- 非常重要 - 即使您的操作引发异常,您也必须保证在操作后解锁等待线程。
  • 如果您的后端分布在多个服务器/JVM上,它将不起作用。

最新更新