我可以按参数同步方法吗

时间:2018-09-25 15:20:32

标签: java multithreading synchronization

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

例如-我让某人使用某种方法,并且我想对某人进行某些操作,但是如果很少有线程为同一个人调用此方法,那么我想一一完成。

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

3 个答案:

答案 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);
        }
    }
  • id 不仅可以是“ Long”,而且可以是任何具有正确覆盖的“ equals”和“ hashCode”方法的类。
  • 最终尝试-非常重要-即使操作引发异常,您也必须保证在操作后解锁等待线程。
  • 如果您的后端分布在多个服务器/ JVM 中,则将无法使用。