首页 > 解决方案 > Java并发-避免并发修改具有相同ID的对象的数据结构

问题描述

对于以下情况,正确的结构是什么:

假设我们有一个库存系统(域并不是很重要,它只是一个示例)并且每个操作都很慢(例如联系外部系统)。

我在想的是我可以处理不会影响相同 WH 的并行请求。例如:

  1. 将 20 个项目从 WH 1 移动到 3 的请求
  2. 将 15 个项目从 2 移动到 5 的移动请求(可以与前一个并行处理)
  3. 将 5 个项目从 3 移动到 6 的请求来了(它应该等待第一个请求完成后再继续)。

我正在考虑一个线程安全映射,其中包含我当前正在处理的仓库的所有 id。

有更好的吗?

标签: javaconcurrency

解决方案


lock我建议,您为每个对象引入一个变量Warehouse以及一个唯一的整数。您可以使用 anAtomicInteger来确保每个创建的仓库都有其唯一编号

public class Warehouse {
    private static final AtomicInteger numberProvider = new AtomicInteger(0);
    private final int number;
    private final Lock lock = new ReentrantLock();
    // ...
    public Warehouse(...) {
        this.number = numberProvider.incrementAndGet();
        ...
    }
    // ... (getter for number and lock and other methods)
}

这样您就可以始终以“正确”的顺序锁定两个仓库(例如,先锁定较低的数字,然后锁定较高的数字;向后解锁)。这将保证您不会遇到死锁。

public void moveStock(Warehouse from, Warehouse to, int nof) {
    List<Lock> locks = Stream.of(from, to)
        .sorted(Comparator.comparingInt(Warehouse::getNumber))
        .map(Warehouse::getLock)
        .collect(Collectors.toList());

    for(int i=0;i<locks.size();++i) {
        locks.get(i).lock();
    }
    try {
        from.substractStock(nof);
        to.addStock(nof);
    } finally {
        for(int i=locks.size()-1;i>=0;i--) {
            locks.get(i).unlock();
        }
    }
}

推荐阅读