Java中对象的锁定机制

时间:2013-11-20 17:21:12

标签: java multithreading

假设客户有一个Credit Card。他的余额为x,他正在购买y有价值的商品(y<x)。他将再次购买另一件物品,费用为z。 (y+z>x but z<x)。

现在我将在Java中模拟这种情况。如果所有事务都按顺序发生,则无需恐慌。客户可以购买y有价值的商品,然后他没有足够的信用额度购买其他商品。

但是当我们进入多线程环境时,我们必须处理一些锁定机制或某些策略。因为如果其他一些线程读取信用卡对象之前反映的变化,以前的线程严重问题将会上升。

据我所知,一种方法是我们可以保留原始余额的副本,我们可以在更新余额之前检查当前值。如果值与原始值相同,那么我们可以确保其他线程不会更改余额。如果平衡不同,那么我们必须撤消计算。

Java Synchronization也是一个很好的解决方案。现在我的问题是在这种情况下实施的最佳方法是什么?

此外,如果我们要全面了解这一点。同步会影响系统的性能。由于它被锁定,因此对象和其他线程必须等待。

4 个答案:

答案 0 :(得分: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在内部保留两个Lock实例。一个保护读访问权限,一个保护写访问权。

答案 1 :(得分:2)

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

唯一的方法是同步。

答案 2 :(得分:2)

你在说什么听起来像software transactional memory.你乐观地认为没有其他线程会修改你的交易所依赖的数据,但你有一个机制来检测它们是否有。

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

正如您所看到的,这种方法受制于活锁线程条件,其他线程不断更改平衡,导致一个线程永远循环。实际上,您的应用程序的细节决定了活锁的可能性。

显然,这种方法很复杂,并且存在陷阱。如果您不是并发专家,那么使用锁来提供原子性会更安全。如果synchronized块内的代码不执行任何阻塞操作(如I / O),则锁定通常表现得足够好。如果关键部分中的代码具有明确的执行时间,那么最好使用锁定。

答案 3 :(得分:0)

  

据我所知,我们可以保留一份原始余额   我们可以在更新余额之前检查当前值。如果   值与原始值相同,那么我们可以确定其他线程   不会改变平衡。如果平衡不同,那么我们必须撤消   我们的计算。

听起来像AtomicInteger.compareAndSet()AtomicLong.compareAndSet()那样。


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

在这种情况下,听起来您希望public synchronized boolean makePurchase(int cost)方法在成功时返回true,在失败时返回false。目标是您的对象上的任何事务都不需要多个方法调用 - 正如您已经意识到的那样,您不希望在CreditCardgetBalance()和更晚{{1}上进行两次方法调用因为潜在的竞争条件而进行交易。