【问题标题】:Issue with synchronizing threads in javajava同步线程的问题
【发布时间】:2018-01-17 22:27:49
【问题描述】:

我正在尝试编写一个示例程序,该示例程序由一个可以修改金额的银行帐户对象和 10 个从属对象组成,这些对象从 2 个不同的银行帐户对象中移动 1 个货币单位一百万次。我的预期结果是银行账户货币的总和最后为 0,就像我开始的那样。我已经尝试在 BankAccount 中同步“setBalance”方法,因为我认为这是应该同步的方法,但它不起作用。当我在 getBalance 上尝试或运行时,即使那些也不起作用,所以我不确定我做错了什么。

BankAccount.java

public class BankAccount{

    private int balance;

    public BankAccount()
    {
        balance = 0;
    }

    public int getBalance()
    {
        return balance;
    }

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

Slave.java

public class Slave extends Thread {

    BankAccount source;
    BankAccount target;

    int currency = 1;

    public Slave (BankAccount source, BankAccount target) {
        this.source = source;
        this.target = target;

    }

    public void run() {
        for (int i = 0; i < 1000000; i++)
        {
            target.setBalance(target.getBalance() + currency);
            source.setBalance(source.getBalance() - currency);
        }
    }
}

Master.java

public class Master {

    public static void main(String[] args) {

        BankAccount account1 = new BankAccount();
        BankAccount account2 = new BankAccount();

        Slave[] slaves = new Slave[10];

        for (int i = 0; i < 10; i++)
        {
            if (i < 5)
            {
                slaves[i] = new Slave(account1, account2);
            }
            else
            {
                slaves[i] = new Slave(account2, account1);
            }
        }

        for (int i = 0; i < 10; i++)
        {
            slaves[i].start();
        }

        try
        {
            for (Slave s : slaves)
                s.join();
        }
        catch (InterruptedException e) {
            System.out.println("Interruption before completion of the joins" + e);
        }


        System.out.println("Sum of balances: " + (account1.getBalance() + account2.getBalance()));
    }
}

【问题讨论】:

  • 您缺少的第一件事是这个问题中的一个示例,说明您如何使用synchronized。
  • @JoeC 是的,忘记包含这个
  • 所以,在getBalance、应用操作(+/-)和setBalance之间,实际余额可能已经改变。最好有一个同步的withdraw 和deposit 方法。这些方法将在单个受保护的步骤中将操作 (+/-) 应用于 balance 本身
  • @MadProgrammer 你是对的,但这只是一个示例程序,让我自己更好地理解“同步”是如何工作的。我想用这段代码来做,不管它有多么不切实际,我相信它应该在某种方法中使用“同步”。
  • @user3487554 在我的脑海中,您需要在 run 方法中同步 target 和 source BankAccount 实例,以确保两者都不能更新.这变得很危险,因为您可能会进入死锁场景

标签: java synchronization synchronized


【解决方案1】:

synchronized 关键字,如果在方法签名中使用,会锁定被调用的对象。如果你想锁定一个替代对象,你需要这样使用关键字:

synchronized(objectToLock) {
    doAtomicStuff();
}

在您的情况下,您的原子操作是获取、调整和设置余额,您需要锁定相关的 BankAccount 对象。

这应该足以让您弄清楚在哪里以及如何使用synchronized。

【讨论】:

    【解决方案2】:

    这段代码...

                target.setBalance(target.getBalance() + currency);
    

    ... 仅当在 T1 的 getBalance() 调用和 setBalance() 的调用之间的不同线程没有修改 target 的余额时,它才会在线程 T1 中产生预期效果。同步其中一种或两种方法并不能防止这种干预修改,而且您的多个线程正在执行如此多的此类修改,如此之快,您很有可能会遇到该问题。

    因此,适当的临界区包含两个方法调用,您可以通过将它们放在synchronized 块中来实现:

                synchronized(target) {
                    target.setBalance(target.getBalance() + currency);
                }
    

    当然,source的更新需要同样对待。请注意,对于每个关键区域,所有竞争线程都需要在同一个对象上进行同步,以使同步产生预期的效果。

    【讨论】:

    • 解释得很好,正是我需要的。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2020-02-01
    • 1970-01-01
    相关资源
    最近更新 更多