我有以下情况。我有一个账户类,每个账户都有余额,可以向其转账。
public class Account {
private int balance;
public Account() {
this.balance = 0;
}
public void transfer(int amount) {
this.balance += amount;
}
@Override
public String toString() {
return "Account (balance: " + balance + ")";
}
}
我有一个转会经理。一次转账只需要两个账户和一笔要转账的钱。传输管理器可以通过将其添加到数组列表(一种传输队列)来发出传输。在将所有传输添加到队列后,可以调用 performTransfers 方法,该方法会在每次传输时调用 performTransfer 方法。
import java.util.ArrayList;
public class TransferManager {
private ArrayList<Transfer> openTransfers;
private int issuedTransfers;
private int performedTransfers;
public TransferManager() {
openTransfers = new ArrayList<Transfer>();
issuedTransfers = 0;
performedTransfers = 0;
}
public void issueTransfer(Account from, Account to, int amount) {
openTransfers.add(new Transfer(from, to, amount));
issuedTransfers++;
}
public void performTransfers() {
for(Transfer transaction : openTransfers) {
transaction.performTransfer();
performedTransfers++;
}
openTransfers.clear();
}
@Override
public String toString() {
return "TransferManager (openTransfers: " + openTransfers.size() + "; issuedTransfers: " + issuedTransfers + "; performedTransfers: " + performedTransfers + ")";
}
private static class Transfer {
private Account from, to;
private int amount;
public Transfer(Account from, Account to, int amount) {
this.from = from;
this.to = to;
this.amount = amount;
}
public void performTransfer() {
from.transfer(-amount);
to.transfer(amount);
}
}
}
现在我添加多线程:
import java.util.Random;
public class BankingTest extends Thread {
private Account[] accounts;
private static Random random = new Random();
public BankingTest(Account[] accounts) {
this.accounts = accounts;
}
public void run() {
final TransferManager manager = new TransferManager();
//simulate some transfers
for(int i = 0; i < accounts.length; i++) {
final int index = i;
Thread thread = new Thread() {
public void run() {
try {
for(int j = 0; j < 10; j++) {
manager.issueTransfer(accounts[index], accounts[(index+1)%accounts.length], 100);
Thread.sleep(random.nextInt(10));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
//wait a bit
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
manager.performTransfers();
System.out.println(manager);
}
}
BankingTest 采用一个数组,例如 10 个空白帐户。现在它还没有同步,我试图理解为什么会出现这些错误:
Exception in thread "Thread-2" java.lang.NullPointerException
at
gp2.ha5.exercise2.TransferManager.performTransfers(TransferManager.java:23)
at gp2.ha5.exercise2.BankingTest.run(BankingTest.java:41)
and
TransferManager (openTransfers: 0; issuedTransfers: 99; performedTransfers:
99)
知道为什么我会收到这些错误以及同步如何帮助解决这个问题吗?
(您可以放大查看详细信息;))
传输管理器:http://pastebin.com/Je4ExhUz
银行测试:http://pastebin.com/cdpWhHPb
开始:http://pastebin.com/v7pwJ5T1
在我将同步添加到 issueTransfer 和 performTransfers 方法后,我不断收到错误:
最佳答案
Ok 很简单,所有线程,尝试执行这个方法:
public void issueTransfer(Account from, Account to, int amount) { openTransfers.add(new Transfer(from, to, amount)); issuedTransfers++; }
但添加到ArrayList 时没有同步。您必须了解,因为列表操作不是原子的,并且因为有多个线程同时访问它,所以几乎所有内容都可以附加并且您列出的内容会损坏。
然后,当您尝试读取您的列表时,您会在其中找到 Null 元素,即使您一开始并没有要求插入一个元素。这是一个示例,说明在未经适当处理的情况下访问相同数据会如何损坏您的数据。
编辑:
因此,每当您拥有共享状态并且想要使用多个线程访问它时,您就必须进行同步。这不仅适用于 issueTransfer 方法。
另一个问题是如何生成线程。 这与您最初的问题无关。
//simulate some transfers for(int i = 0; i < accounts.length; i++) { final int index = i; Thread thread = new Thread() { public void run() { try { for(int j = 0; j < 10; j++) { manager.issueTransfer(accounts[index],
accounts[(index+1)%accounts.length], 100); Thread.sleep(random.nextInt(10)); } } catch (InterruptedException e) {
e.printStackTrace(); } } }; thread.start();
从此处的代码中,所有线程都访问一个全局状态:用于访问数组中帐户的索引。但是主线程使用 for 循环递增,而线程可以随时执行。很有可能在线程启动时索引值已更改。
如您所见,当您没有对它给予足够的重视时,并发总是困扰您 ;)
关于java - 线程银行转账模拟和同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5926873/