【问题标题】:Bank Account/User Application: Multi-threading and Synchronization银行账户/用户应用程序:多线程和同步
【发布时间】:2018-01-16 14:07:46
【问题描述】:

对于我目前的 uni 课程作业,我打算创建一个 Java 控制台应用程序来模拟创建最多 4 个用户可以同时访问的共享银行帐户。借助我在网上找到的所有资源和其他多线程应用程序,我能够连接我当前的代码。

这是一种心态 - 每个用户都应该可以平等地访问银行账户,而不会出现延迟、死锁、等待等情况。执行的每笔交易(取款和存款)都应该更新到银行余额并记录下来。为每个用户执行的特定事务存储在数组列表中。

我将制作应用程序的非同步版本和同步版本。我通过创建 4 个类来解决这个问题 - BankAccount(银行账户)、BankTester(运行主应用程序的测试器类)、User(用户)和 UserThread(用户的线程类)。

我现在拥有的是应用程序的非同步版本。关于如何同步(并改进此非同步版本)的任何帮助都会对我现在对多线程有所帮助。谢谢大家。

这是我的代码。

BankAccount 类:

public class BankAccount {

private long accountNo;
private double accountBalance;

public BankAccount() {
    this.accountNo = 9876543210L;
    this.accountBalance = 1980;
}

public void getAccountInfo() {
    System.out.println("Account Number: " + accountNo);
    System.out.println("Starting Account Balance: " + accountBalance);
}

public void getAccountBalance() {
    System.out.println("Account Balance: " + accountBalance);
}

public void deposit(double value) {
    accountBalance = (accountBalance + value);
     System.out.println("Account Balance After Deposit: " + accountBalance);
}

public void withdraw(double value) {
    accountBalance = (accountBalance + value);
    System.out.println("Account Balance After Withdrawal: " + accountBalance);
}
}

BankTest 类:

import java.util.Scanner;

public class BankTest {

private static double[][] transactionList = {
    {50, 10, -20, 10, -20, 20, 10, 50, -10, 10, -10, 50},
    {20, 20, -20, 50, -20, 10, 50, 50, -20, 10, 10},
    {50, 10, 10, -10, -10, 50, 20, -10, -20},
    {50, 10, -20, 20, 10, -20}
};

private static BankAccount myAccount;

private static UserThread myUser1;
private static UserThread myUser2;
private static UserThread myUser3;
private static UserThread myUser4;

public static void main(String[] args) {

    for (;;) {
        System.out.println("<--------- Banking Menu ----------->");
        System.out.println("1. Create Bank Account");
        System.out.println("2. Create User");
        System.out.println("3. Run Simulation");
        System.out.println("4. Exit");
        System.out.println("Enter choice: ");

        Scanner in = new Scanner(System.in);

        switch (in.nextInt()) {
            case 1:
                System.out.println("<--------- Create Bank Account ----------->");
                myAccount = new BankAccount();

                System.out.println("Bank Account Created!");
                break;

            case 2:
                System.out.println("<--------- Create User ----------->");
                System.out.println("1. User 1");
                System.out.println("2. User 2");
                System.out.println("3. User 3");
                System.out.println("4. User 4");
                userMenu();
                break;

            case 3:
                myUser1.start();
                myUser2.start();
                myUser3.start();
                myUser4.start();
                break;

            case 4:
                System.out.println("Goodbye");
                System.exit(0);
                break;

            default:
                System.err.println("Unrecognized option");
                break;
        }
    }
}

public static void userMenu() {
    Scanner in = new Scanner(System.in);

    switch (in.nextInt()) {
        case 1:
            System.out.println("<----- User 1 ------>");
            User user1 = new User("Saul", "Goodman", myAccount, transactionList[0]);
            myUser1 = new UserThread(user1, "User 1 Thread");
            user1.getUserInfo();
            break;

        case 2:
            System.out.println("<-------- User 2 ------->");
            User user2 = new User("Walter", "White", myAccount, transactionList[1]);
            myUser2 = new UserThread(user2, "User 2 Thread");
            user2.getUserInfo();
            break;

        case 3:
            System.out.println("<-------- User 3 ------->");
            User user3 = new User("Jessie", "Pinkman", myAccount, transactionList[2]);
            myUser3 = new UserThread(user3, "User 3 Thread");
            user3.getUserInfo();
            break;

        case 4:
            System.out.println("<-------- User 4 ------->");
            User user4 = new User("Hank", "Schrader", myAccount, transactionList[3]);
            myUser4 = new UserThread(user4, "User 4 Thread");
            user4.getUserInfo();
            break;

        default:
            System.err.println("Unrecognized option");
            break;
    }
}

public static void runSimulation(User u) {
    double[] tList = u.getTransactionList();
    BankAccount ba = u.getBankAccount();

    for (int i = 0; i < tList.length; i++){
        if(tList[i] < 0) {
            ba.withdraw(tList[i]);
        } else{
            ba.deposit(tList[i]);
        }
        u.getName();
        ba.getAccountBalance();
    }
}
}

用户类别:

public class User {

private String name;
private String surname;
private BankAccount bankAccount;

private double[] transactionList;

public User(String n, String s, BankAccount bA, double[] tL) {
    this.name = n;
    this.surname = s;
    this.bankAccount = bA;
    this.transactionList = tL;
}

public void getName() {
    System.out.println("Name: " + this.name);
}

public double[] getTransactionList(){
    return transactionList;
}

public void getSurname() {
    System.out.println("Surname: " + this.surname);
}

public void getUserInfo() {
    System.out.println("Full name: " + this.name + " " + this.surname);
}

public BankAccount getBankAccount() {
    return bankAccount;
}   
}

UserThread 类:

public class UserThread extends Thread {

private User u;

public UserThread(User u, String name) {
    super(name);
    this.u = u;
}

@Override
public void run() {
    BankTest.runSimulation(this.u);
}
}

【问题讨论】:

标签: java multithreading synchronization thread-synchronization


【解决方案1】:

您应该使用信号量来控制对临界区的访问。我猜每个用户都可以访问他们的getAccountInfo(),但如果两个用户可以访问同一个银行账户,其他方法(如withdrawdeposist)应该有一些控制权。

在你的getAccountInfo() 中你也有这个:

public void getAccountInfo() {
    System.out.println("Account Number: " + accountBalance);
    System.out.println("Starting Account Balance: " + accountBalance);
}

但是应该是这样的:

public void getAccountInfo() {
    System.out.println("Account Number: " + accountNo);
    System.out.println("Starting Account Balance: " + accountBalance);
}

编辑:

如果您只有一个用户访问银行帐户,那么您无需控制任何内容。然而,想象一下我们在一个银行账户中有 100 美元的余额。如果两个用户“同时”尝试withdraw,那么两个用户都将获得 100 美元。所以,你应该控制这种行为。使用semaphores 会对您有所帮助。

【讨论】:

  • 感谢指正。信号量?老实说,从来没有听说过。现在让我研究一下。
  • 我读过一点,所以应该有一个变量来控制4个有权提款或存款的用户?
  • @AuduIbrahim 对!我更新了我的答案,希望对您有所帮助!
  • 谢谢大佬,我想这对我的水平来说可能太高级了。不过,我会继续研究如何实现信号量!!!
【解决方案2】:

任何关于我如何...改进这个非同步版本的帮助

通常,当方法以getset 开头时,该方法应分别返回 或设置一个值。您的 get 方法仅打印到控制台。假设作业中没有明确说明行为,我会修改它们,以便它们返回名称中指示的值:

public double getAccountNumber() {
    return accountNo;
}

public double getAccountBalance() {
    return accountBalance;
}

您可以在BankAccount 实例检索这些详细信息后打印出这些详细信息。如果您愿意,可以将这些交互封装在某种 BankingSession 类中。

作业是否指定了您应如何处理可能导致负余额的交易?如果允许用户有负余额,那么depositwithdraw 应该永远不会失败。但如果他们不能,您应该发出交易是否成功的信号。有几种方法可以做到这一点。一种方法是将withdraw 方法的返回类型从void 更改为boolean,并且仅在提款成功完成时才返回true(意味着有足够的可用资金)。否则,返回false。如果事务无法完成,另一种选择是抛出异常(如自定义 InsufficientFundsException)。

关于如何同步它的任何帮助

只有两种方法可以修改银行账户的状态:withdrawdeposit。由于您是相互独立地调用它们(每次存款或取款都是一个独立的操作),您只需要确保一次只能发生其中一个。在方法声明中添加 synchronized 关键字就足够了,例如:

public synchronized void deposit(double value) { ... }
public synchronized void withdraw(double value) { ... }

【讨论】:

  • 用户不允许有负余额,所以我想在withdraw方法中应该有一个if语句(在BankAccount类中)?
  • 还是一试身手?
  • 这是我所做的,以防出现负余额,这行得通吗? public synchronized void withdraw(double value) { if (accountBalance > 0) { System.out.println("提取的金额:" + (int) -(value)); System.out.println("当前余额:" + (accountBalance = (accountBalance + value))); } else { System.out.println("余额不足!!!"); } }
  • 几个想法:(1) 对value 进行健全性检查——它是一个正数吗? (2) 一般而言,旨在执行操作的函数不应将其结果打印到控制台;只需返回成功或失败代码,并在必要时让调用者打印结果。
  • 我不知道我将如何实现 1。对于 2,在 else 语句中,'return false'?
猜你喜欢
  • 2019-07-22
  • 2016-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-27
相关资源
最近更新 更多