Java中对象的锁定机制

4
假设客户有一张信用卡。他的余额是 x 元,他正在购买价值为 y 的商品(yx 但 z
现在我将在 Java 中模拟这种情况。如果所有交易都是顺序进行的,则无需恐慌。客户可以购买价值为 y 的商品,然后他没有足够的信用来购买其他商品。
但是,当我们进入多线程环境时,我们必须处理一些锁定机制或策略。因为如果另一个线程在先前线程反映更改之前读取信用卡对象,则会出现严重问题。
据我所知,一种方法是我们可以保留原始余额的副本,并在更新余额之前检查当前值。如果该值与原始值相同,则我们可以确保其他线程不会更改余额。如果余额不同,则必须撤销计算。
Java 同步也是一个很好的解决方案。现在我的问题是,在这种情况下实施最佳方法是什么?
此外,如果我们要从宏观角度看待这个问题。同步会影响系统的性能。由于它锁定了对象,其他线程必须等待。

你听说过synchronized吗? - Rahul Bobhate
@RahulBobhate,我已经将那部分添加到问题中了。 - Ruchira Gayan Ranaweera
其他线程只有在尝试访问相同的信用卡时才需要等待,这种情况非常不太可能发生。 - Alexei Kaigorodov
@AlexeiKaigorodov 是的,你说得对。但如果这是大规模的,并且有更多的进程,那么问题就会出现。 - Ruchira Gayan Ranaweera
4个回答

3

我将使用ReadWriteLock,这有助于读写锁定,这很好,因为您可以为每个资源拥有单独的读写锁:

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();


readWriteLock.readLock().lock();

    // multiple readers can enter this section
    // if not locked for writing, and not writers waiting
    // to lock for writing.

readWriteLock.readLock().unlock();


readWriteLock.writeLock().lock();

    // only one writer can enter this section,
    // and only if no threads are currently reading.

readWriteLock.writeLock().unlock();

ReadWriteLock 内部保持两个锁实例。一个用于读取访问,另一个用于写入访问。


我期待着更多地了解这个。感谢您的回答和点赞。 - Ruchira Gayan Ranaweera

2
你所谈论的听起来像是软件事务内存。你乐观地假设没有其他线程会修改你的事务所依赖的数据,但你有一种机制可以检测到它们是否已经修改。 java.util.concurrent.atomic 包中的类型可以帮助构建无锁解决方案。它们实现了高效的比较和交换操作。例如,一个 AtomicInteger 引用将允许你像这样做:
AtomicInteger balance = new AtomicInteger();

…

void update(int change) throws InsufficientFundsException {
  int original, updated;
  do {
    original = balance.get();
    updated = original + change;
    if (updated < 0)
      throw new InsufficientFundsException();
  } while (!balance.compareAndSet(original, update));
}

如您所见,这种方法容易出现线程活锁的情况,其中其他线程不断改变余额,导致一个线程无限循环。在实践中,应用程序的具体情况决定了活锁的可能性。
显然,这种方法非常复杂且充满陷阱。如果您不是并发专家,使用锁来提供原子性更加安全。如果同步块内的代码不执行任何阻塞操作(例如I/O),则锁定通常足够快。如果关键部分的代码有明确的执行时间,则最好使用锁。

2

您的建议不适合。在检查和更新之间无法确定上下文切换是否发生。

唯一的方法是同步。


0
据我所见,一种方法是我们可以保留原始余额的副本,并在更新余额之前检查当前值。如果该值与原始值相同,则可以确保其他线程不会更改余额。如果余额不同,则必须撤消我们的计算。
听起来像是 AtomicInteger.compareAndSet()AtomicLong.compareAndSet() 所做的事情。

更易理解的方法是在您的CreditCard类上使用synchronized方法,您的代码将调用这些方法来更新余额。(一个对象上只能执行一个synchronized方法。)

在这种情况下,您需要一个public synchronized boolean makePurchase(int cost)方法,在成功时返回true,失败时返回false。目标是您的对象上没有任何交易需要超过一个方法调用 - 正如您已经意识到的那样,您不想在CreditCard上进行两个方法调用(getBalance()和稍后的setBalance())来完成交易,因为可能会出现竞争条件。


最好锁定资源,而不仅仅是方法。因为这仍然会导致“竞态条件”。 - tokhi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接