为什么需要在课堂上同步

发布于 2025-02-12 05:34:19 字数 1472 浏览 0 评论 0原文

我有以下示例,这些示例试图在2个帐户之间转移资金,我在想一个同步 就足够了,我几次运行了示例,发现总数不正确:

public class Test {
  static Random random = new Random();
  public static int randomAmount() {
    return random.nextInt(100) + 1;
  }

  public static void main(String[] args) throws InterruptedException {
    Account a = new Account(1000);
    Account b = new Account(1000);

    Thread t1 = new Thread(() -> {
      for (int i = 0; i < 100; i++) {
        a.transfer(b, randomAmount());
      }
    });

    Thread t2 = new Thread(() -> {
      for (int i = 0; i < 100; i++) {
        b.transfer(a, randomAmount());
      }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();

    System.out.println("Total: " + (a.getBalance() + b.getBalance()));
  }
}

class Account {
  private int balance;

  public Account(int balance) {
    this.balance = balance;
  }

  public int getBalance() {
    return balance;
  }

  public void setBalance(int balance) {
    this.balance = balance;
  }

  public void transfer(Account target, int amount) {
    synchronized (this) {
      if (this.balance >= amount) {
        this.setBalance(this.getBalance() - amount);
        target.setBalance(target.getBalance() + amount);
      }
    }
  }
}

同步 on account.class.class将解决此问题,因为只有一个线程可以随时输入Transfer方法。但是,如果我只同步(this),我将无法弄清楚何时会导致多个线程导致不正确的总数。

I have the following example which tries to transfer money between 2 account, I was thinking a synchronized on this is enough, I ran the example several time and found the total is incorrect:

public class Test {
  static Random random = new Random();
  public static int randomAmount() {
    return random.nextInt(100) + 1;
  }

  public static void main(String[] args) throws InterruptedException {
    Account a = new Account(1000);
    Account b = new Account(1000);

    Thread t1 = new Thread(() -> {
      for (int i = 0; i < 100; i++) {
        a.transfer(b, randomAmount());
      }
    });

    Thread t2 = new Thread(() -> {
      for (int i = 0; i < 100; i++) {
        b.transfer(a, randomAmount());
      }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();

    System.out.println("Total: " + (a.getBalance() + b.getBalance()));
  }
}

class Account {
  private int balance;

  public Account(int balance) {
    this.balance = balance;
  }

  public int getBalance() {
    return balance;
  }

  public void setBalance(int balance) {
    this.balance = balance;
  }

  public void transfer(Account target, int amount) {
    synchronized (this) {
      if (this.balance >= amount) {
        this.setBalance(this.getBalance() - amount);
        target.setBalance(target.getBalance() + amount);
      }
    }
  }
}

synchronized on Account.class will fix this since only one thread will be able to enter transfer method at any time. But I am not able to figure out when multiple thread will cause the incorrect total if I only synchronized(this).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

べ映画 2025-02-19 05:34:19

想象一下此情况:

   Account a = new Account(1000);
   Account b = new Account(1000);
   Account c = new Account(1000);

使用两个线程:

  // Thread X transfers 50 from a to b
  // Thread Y transfers 50 from b to c

线程X的调用A.Tranfer(b,50)将同步a

线程Y的调用B. Transfer(C,50)将同步b

请注意,它们正在同步不同的帐户,因此两个呼叫都可以同时执行同步块。

因此,各种种族条件可能发生。例如:

  • 一个帐户有足够的钱时可以检查转移,但是到转移完成时,余额就已经消失了。从帐户的余额产生否定为负。


  • 金钱可能是“魔术”,因为

      this.setBalance(this.getBalance() - 量);
     

    不是原子操作。特别是,另一个线程可以更改getBalance()setBalance()呼叫之间的平衡。

另外,所需的发生在不存在各个线程之间的关系之前,因此在阅读this.balance时,线程可能会看到过时的值。


锁定account.class是一种可能的解决方案。

另一个可能的解决方案是在执行传输时锁定两个帐户。 (但是,如果您这样做,您需要防止僵局。)

Imagine this scenario:

   Account a = new Account(1000);
   Account b = new Account(1000);
   Account c = new Account(1000);

with two threads:

  // Thread X transfers 50 from a to b
  // Thread Y transfers 50 from b to c

Thread X's call to a.tranfer(b, 50) will synchronize on a.

Thread Y's call to b.transfer(c, 50) will synchronize on b.

Note that they are synchronizing on different accounts, so both calls can execute the synchronized block simultaneously.

So it is possible for various race conditions to occur. For example:

  • A transfer could be checked when one account has enough money, but by the time the transfer is done, the balance could already be gone. Resulting in the from account's balance going negative.

  • Money could be "magic'd" into or out of existence because

    this.setBalance(this.getBalance() - amount);
    

    is not an atomic operation. In particular, another thread could change the balance between the getBalance() and setBalance() calls.

In addition, the required happens before relationships between the respective threads are not present, so it is possible that threads will see stale values when reading this.balance.


Locking on Account.class is one possible solution.

Another possible solution is to lock both accounts while performing a transfer. (But you need to guard against deadlocks, if you do it this way.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文