У меня есть простой код в Java. Целью является проверка входной блокировки () Java. Есть класс с именем Bank (код ниже), который содержит массив объектов Account (код для класса Account ниже).
Класс Bank может переводить деньги с одного счета на другой счет, используя методы объектов Account, названные снять конечный депозит. (Одним из членов данных является массив учетных записей с именами учетных записей).
AccountThread расширяет поток, который будет запускать каждую учетную запись как поток. Он содержит ссылку на объект «Банк».
TestBank имеет метод (publi c stati c void main (String args []) и будет запускать объекты.
It инициализирует объект Bank, создаст 10 объектов AccountThread, передаст ссылку на этот объект Bank этим объектам AccountThread, затем он запустит все эти 10 потоков (с объектами AccountThread).
Эти 10 учетных записей переводят только деньги друг с другом. Каждый поток будет иметь свою собственную учетную запись.
Эти потоки будут бесконечно l oop, случайным образом выбирают учетную запись (одну из 9 других учетных записей) и переводят случайную сумму на эту учетную запись.
После каждых 10 000 транзакций программа выводит некоторые данные, одним из которых является «сумма баланса» (сумма баланса всех этих 10 счетов).
Эта сумма никогда не должна меняться , поскольку счета переводят деньги друг другу.
Я использовал lock.lock () и lock.unlock () в методах «депозит» и «снятие», так что пока есть пополнение счета или снятие средств с одного аккаунта, другие потоки не должны получать доступ к этим методам.
Но есть некоторые проблемы:
Но проблема в том, что я получаю некоторые отклонения от суммы Я не понимаю, с чем это связано.
Я пытался использовать "AccountThread", поскольку класс реализует Runnable, но результаты те же.
Вот что я пытался:
1) И я подозревал, что причиной этого может быть то, что потоки все еще работают при печати данных (что может привести к несогласованности данных). Поэтому я изменил что-то в методе передачи в классе Bank, сделал код, чтобы остановить все потоки до запуска test () (и разблокировать все после выполнения test ()).
for (Account account: accounts) {account.getLock().lock();}
перед вызовом test () и toString ()
и после test () и toString () я позвонил
for (Account account: accounts) {account.getLock().unlock();}
Но результаты совпадают.
2) Я заблокировал только две учетные записи, которые только использовались при передаче перед запуском test (), чтобы другие потоки не использовали их для учетных записей. Но это тоже не работает.
Я пробовал и другие комбинации, но результаты не являются желаемыми.
Есть две проблемы:
-
Другая проблема заключается в том, что методы toString () и test () вызываются после друг друга, при запуске программы эти методы вызываются в случайном порядке **
Третий Проблема заключается в том, что результаты теста () отображаются не после каждых 10 000 транзакций, но часто более 10 000 транзакций
Могут ли несколько потоков обращаться к одному и тому же методу deposit () илиdraw () из одного класса? в то же время, несмотря на то, что методы используют методы "lock.lock () и" lock.unlock () "?
Почему здесь есть какое-то отклонение? Как я могу убедиться, что общий баланс всегда одинаковы?
Вот класс Account:
import java.util.concurrent.locks.*;
class Account {
private int balance;
private int accountNumber;
private Lock lock;
private Condition lockCondition;
public Account(int accountNumber, int balance) {
this.accountNumber = accountNumber;
this.balance = balance;
this.lock = new ReentrantLock();
this.lockCondition = lock.newCondition();
}
/* HERE IS THE WITHDRAW AND DEPOSIT METHODS THAT ARE LOCKED */
void withdraw(int amount) {
lock.lock(); // Acquire the lock
try {
while (balance < amount) {
lockCondition.await();
}
balance -= amount;
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
finally {
lock.unlock(); // Release the lock
}
}
void deposit(int amount) {
lock.lock(); // Acquire the lock
try {
balance += amount;
// Signal thread waiting on the condition
lockCondition.signalAll();
}
finally {
lock.unlock(); // Release the lock
}
}
int getAccountNumber() {
return accountNumber;
}
public int getBalance() {
return balance;
}
Lock getLock() {
return lock;
}
}
Вот класс AccountThread:
import java.util.Random;
class AccountThread extends Thread {
private Bank bank;
private boolean debug;
private int accountIndex;
private int maxTransferAmount;
private Random random;
public AccountThread(Bank b, int index, int max, boolean debug) {
this.bank = b;
this.accountIndex = index;
this.maxTransferAmount = max;
this.debug = debug;
this.random = new Random();
}
public void run() {
try {
while (!interrupted()) {
for (int i = 0; i < maxTransferAmount; i++) {
int toAccount = random.nextInt(bank.nrOfAccounts());
int amount = random.nextInt(maxTransferAmount);
bank.transfer(accountIndex, toAccount, amount);
sleep(2);
}
}
} catch (InterruptedException ignored) {
}
}
}
Вот класс банка:
import java.util.concurrent.locks.*;
class Bank {
private static final int TEST_FREQUENCY = 10000;
private static final int TO_STRING_FREQUENCY = 10000;
private Lock lock;
private int deviationCount;
private int initialBalance;
private Account[] accounts;
private long transactionCount;
private boolean debug;
private int testCount;
public Bank(int accountAmount, int initialBalance, boolean debug) {
accounts = new Account[accountAmount];
this.initialBalance = initialBalance;
this.debug = debug;
int i;
for (i = 0; i < accounts.length; i++)
accounts[i] = new Account(i, initialBalance);
this.transactionCount = 0;
this.deviationCount = 0;
this.lock = new ReentrantLock();
}
public void transfer(int fromAccount, int toAccount, int amount) {
accounts[fromAccount].withdraw(amount);
accounts[toAccount].deposit(amount);
this.transactionCount++;
// accounts[fromAccount].getLock().lock();
// accounts[toAccount].getLock().lock();
// for (Account account: accounts) {account.getLock().lock();}
lock.lock();
try {
if (transactionCount % TEST_FREQUENCY == 0) {
test();
}
if (transactionCount % TO_STRING_FREQUENCY == 0) {
System.out.println(toString());
}
// accounts[fromAccount].getLock().unlock();
// accounts[toAccount].getLock().unlock();
} finally {
lock.unlock();
}
// for (Account account: accounts) {account.getLock().unlock();}
}
public void test() {
int sum = 0;
for (Account account : accounts) {sum += account.getBalance(); }
if (sum != nrOfAccounts()*initialBalance) {deviationCount++; }
System.out.println("Transactions:" + getTransactionCount() + " Balance: " + sum
+ " Deviation count: " + getDeviationCount());
testCount++;
}
@Override
public String toString() {
String string = String.format("\nTransactions; %d%n"
+ "Initial balance: %d%nNumber of accounts: %d%n"
+ "Deviation count: %d%nTestCount: %d%n"
+ "Error percentage: %.2f%n",
getTransactionCount(), initialBalance, nrOfAccounts(),
getDeviationCount(), testCount, getErrorPercentage());
if (debug) {
for (Account account :accounts) {
string = string.concat(String.format("Account nr.: %d, Balance: %d%n",
account.getAccountNumber(), account.getBalance()));
}
}
return string;
}
int nrOfAccounts() {
return accounts.length;
}
private long getTransactionCount() {
return transactionCount;
}
private int getDeviationCount() {
return deviationCount;
}
private double getErrorPercentage() {
double dividend = getDeviationCount();
double divisor = testCount;
double result = dividend / divisor;
return result;
}
}
Вот класс BankTest:
public class BankTest {
private static final boolean DEBUG = true;
private static final int ACCOUNT_AMOUNT = 10;
private static final int INITIAL_BALANCE = 100000;
public BankTest() {};
public static void main(String[] args) {
Bank b = new Bank(ACCOUNT_AMOUNT, INITIAL_BALANCE, DEBUG);
int i;
for (i = 0; i < ACCOUNT_AMOUNT; i++) {
AccountThread t = new AccountThread(b, i,
INITIAL_BALANCE, DEBUG);
t.setPriority(Thread.NORM_PRIORITY + i % 2);
t.start();
}
}
}