java - Java 中的同步银行模拟器

标签 java multithreading synchronization thread-synchronization

我有一种方法transfer(),它可以从一个帐户提取资金并将其存入另一个帐户。有 10 个帐户,每个帐户都运行自己的线程。我有另一种方法 test() ,它将每个帐户中的总额相加,以确保银行没有损失或盈利。为了获得准确的总数,我创建了一个 boolean 标志来指示测试是否正在进行。如果是,我需要以某种方式暂停传输,直到测试完成。我尝试使用同步块(synchronized block)来实现此目的,以告诉线程等待条件并在条件不再成立时释放。由于某种原因我遇到了困难。我的转账方法如下:

public class Bank {

    public static final int NTEST = 10;
    private Account[] accounts;
    private long ntransacts = 0;
    private int initialBalance;
    private int numAccounts;
    private boolean open;
    private int transactsInProgress;
    private boolean testing=false;

    public Bank(int numAccounts, int initialBalance) {
        open = true;
        this.initialBalance = initialBalance;
        this.numAccounts = numAccounts;
        accounts = new Account[numAccounts];
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = new Account(this, i, initialBalance);
        }
        ntransacts = 0;
        transactsInProgress = 0;
    }
    public synchronized void incrementTransacts(){
        transactsInProgress++;
    }
    public synchronized void decrementTransacts(){
        transactsInProgress--;
    }

    public void transfer(int from, int to, int amount) throws InterruptedException {

    accounts[from].waitForAvailableFunds(amount);
    synchronized(this){
        while(testing){
            System.out.println("Cannot transfer while testing...");
            this.wait();
        }
    }
        if (!open) return;
        if (accounts[from].withdraw(amount)) {
            incrementTransacts(); //synchronzied method increments transactsInProgress
            accounts[to].deposit(amount);
            decrementTransacts(); //synchronized method
        }
        if (shouldTest()) test();

    synchronized(this){
        this.notifyAll();
    }    
    }

    public synchronized void test() throws InterruptedException {
        int sum = 0;

        testing=true;
        while(transactsInProgress!=0){
                System.out.println("Cannot test while transactions are in progres... \nWaiting...");
            wait();
        }

        for (int i = 0; i < accounts.length; i++) {
            System.out.printf("%s %s%n", 
                    Thread.currentThread().toString(),accounts[i].toString());
            sum += accounts[i].getBalance();
        }
        System.out.println(Thread.currentThread().toString() + 
                " Sum: " + sum);
        if (sum != numAccounts * initialBalance) {
            System.out.println(Thread.currentThread().toString() + 
                    " Money was gained or lost");
            System.exit(1);
        } else {
            System.out.println(Thread.currentThread().toString() + 
                    " The bank is in balance");
        }
        testing=false;
        notifyAll();
    }
       public int size() {
        return accounts.length;
    }

    public synchronized boolean isOpen() {return open;}

    public void closeBank() {
        synchronized (this) {
            open = false;
        }
        for (Account account : accounts) {
            synchronized(account) {
                account.notifyAll();
            }
        }
    }

    public synchronized boolean shouldTest() {
        return ++ntransacts % NTEST == 0;
    }
}

自从我用 Java 编码以来已经有一段时间了,而且我对线程和并发还很陌生,所以我不确定我到底哪里出了问题。当我运行该程序时,银行金额不正确。每个账户有10,000,所以每次的总和应该是100,000。这里有什么想法吗?

编辑:线程类和主要:

class TransferThread extends Thread {

    public TransferThread(Bank b, int from, int max) {
        bank = b;
        fromAccount = from;
        maxAmount = max;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            int toAccount = (int) (bank.size() * Math.random());
            int amount = (int) (maxAmount * Math.random());
            bank.transfer(fromAccount, toAccount, amount);
        }
        bank.closeBank();
    }
    private Bank bank;
    private int fromAccount;
    private int maxAmount;
}

主要:

public static void main(String[] args) throws InterruptedException {
    Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
    Thread[] threads = new Thread[NACCOUNTS];
    // Start a thread for each account
    for (int i = 0; i < NACCOUNTS; i++) {
        threads[i] = new TransferThread(b, i, INITIAL_BALANCE);
        threads[i].start();
    }
    // Wait for all threads to finish
    for (int i = 0; i < NACCOUNTS; i++) {
        try {
            threads[i].join();
        } catch (InterruptedException ex) {
            // Ignore this
        }
    }
    b.test();
}

最佳答案

我不知道您的确切问题,但您的代码中有一些有关的内容:

  1. 您的 transfer() 方法有两个不同的 synchronized block ,但似乎执行的操作应在它们之间受到保护。

  2. 不要信任原始 boolean 变量进行同步。当您使用多个线程时,您应该使用 AtomicBoolean

现在更新我对问题有了更好的理解:

这里的问题是您尝试以一种不符合其设计者意图的方式使用synchronized。如果要同步,您可以选择一个对象并说“一次只有一个线程可以操作这个对象”。在 Bank 类中使用 synchronized(this) 或将方法声明为 synchronized 表示“一次只有一个线程可以操作银行的状态” 。

从您下面的评论中,我了解到情况并非如此。如果多个线程可以同时更新帐户,则银行不是您想要同步的资源。

您应该在更细粒度的级别进行保护(例如,单独锁定每个帐户),或者使用不同的锁定结构,例如 ReadWriteLock它允许多个线程共享较低级别的访问权限或单个线程获得独占访问权限。

关于java - Java 中的同步银行模拟器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32599547/

相关文章:

linux - 如何使 FIO 重播多线程跟踪

java - 使用 "notify()"& "wait()"而不是 "suspend()"和 "resume()"来控制线程

Java返回值、对象引用

具有可变维数组的 Java 对象

java - while(true) 循环或标准程序循环的 java.util.Timer?

Java - 使用具有线程间通信的两个线程打印数字序列

.net - 通缉 : Cross-process synch that doesn't suffer from AbandonedMutexException

java - 用两个线程循环打印数字

java - 如何在java中使用dom解析器按属性获取元素

c#为什么要把对象放在lock语句中