我可以按参数同步方法吗?
例如-我让某人使用某种方法,并且我想对某人进行某些操作,但是如果很少有线程为同一个人调用此方法,那么我想一一完成。
private void dosomething(Long id, Person person) {
dosomethingelse(id, person);
}
如何仅针对相同的ID分别调用dosomethingelse(ID,人)?但我希望可以将用于不同ID的代码称为多线程
我写了这段代码,但也许这里有些错误,或者可能会更好。
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); }
);
}
答案 0 :(得分:2)
使用CountdownLatch
的问题是您不能“增加”计数,因此在使用现有闩锁时需要替换它,这会使代码复杂化。
您可以将a 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();
}
答案 1 :(得分:0)
您可以在传递的参数上使用synchronized
关键字(罪魁祸首:它不能为null!)。而且,这还使您不必担心重新获得锁(它是可重入的)。
因此实现方式如下:
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.
}
答案 2 :(得分:0)
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);
}
}