java - 在减去值时无法控制 AtomicInteger 的原子行为

标签 java multithreading atomicinteger

这是我第一次在多线程中使用java.util.concurrent.atomic包进行同步。我试图通过 java.util.concurrent.atomic 处理 Kathy Sierra 书中为 OCP 提供的 AccountDanger 类的经典示例。但我无法保护 balance 变量,即 AtomicInteger 不被过度撤回。下面是我的代码:

帐户类别

package p1;

import java.util.concurrent.atomic.AtomicInteger;

public class Account {
  private AtomicInteger balance = new AtomicInteger(100);

  public int getBalance() {
    return balance.get();
  }

  public void withdraw(int amount) {
    balance.addAndGet(-amount);
    System.out.println("~~~~~~ " + balance);
  }
}

AccountDanger 类

package p1;

public class AccountDanger implements Runnable {
  private Account account = new Account();
  private int amt = 10;

  public void run() {

    for (int i = 0; i < 10; i++) {
      if (account.getBalance() >= amt) {
        System.out.println(Thread.currentThread().getName() + " is going to withdraw..");
        try {
          Thread.sleep(500);
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        account.withdraw(amt);
      } else {
        System.out.println("not enough balance");
      }
      if (account.getBalance() < 0) {
        System.out.println("account is over withdrawn!!!");
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    AccountDanger ad = new AccountDanger();
    Thread t1 = new Thread(ad, "Mark");
    Thread t2 = new Thread(ad, "Phew");
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println("final balance left is : " + ad.account.getBalance());
  }
}

我知道,我肯定有哪里错了。任何人都可以纠正我的解决方案..

最佳答案

您没有以原子方式使用 AtomicInteger(或者更确切地说,Account)。

if(account.getBalance()>=amt){
   // Other stuff
   account.withdraw(amt);

其他线程可以改变“其他东西”内部的平衡。

Account中,您需要一个像这样的方法,使用 AtomicInteger.compareAndSet :

boolean withdrawIfBalanceEquals(int balance, int amt) {
  return this.balance.compareAndSet(balance, balance - amt);
}

仅当 AtomicInteger 的值仍等于 balance 时,才将其设置为 balance-amt,并且然后返回true。如果自从您读取其值后它已更改,compareAndSet 将返回 false

然后,在 AccountDanger 中,您可以像这样使用它:

int balance = account.getBalance();
if (balance >= amt) {
  // Other stuff.
  account.withdrawIfBalanceEquals(balance, amt);
}

我让您决定如何最好地处理 withdrawIfBalanceEquals 返回 false 的情况;一种方法是:

while (true) {
  int balance = account.getBalance();
  if (balance >= amt) {
    // Other stuff.
    if (account.withdrawIfBalanceEquals(balance, amt)) {
      // If true, we don't have to keep looping: we withdrew the
      // balance.
      break;
    }

    // If false, keep going: you'll read the balance again,
    // check if it's still greater than balance etc.

  } else {
    System.out.println("Not enough balance");
    break;
  }
}

关于java - 在减去值时无法控制 AtomicInteger 的原子行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48093529/

相关文章:

java - 方法是否需要同步?

java - 如何在java中的抽象类中实现接口(interface)并扩展线程

应用 java AtomicIntegeraccumulateAndGet

java - Atomic Integer incrementAndGet() 线程安全吗?

java - AtomicInteger 如何是线程安全的

java - 我们可以添加 Maven 插件而不将其目标附加到特定阶段吗?

java - 为什么没有捕获到这个异常

Java排序ArrayList<Integer>

java - 在 Eclipse 中访问图像路径

C++ 伪随机数生成器线程安全吗?