Многотопоточность в Java: как корректно перемещать средства между аккаунтами?
Собственно, пытаюсь сделать перевод между условными аккаунтами в многопоточной среде, на сколько верно пытаюсь реализовать?
По задумке я создаю новый поток в нем инициализирую новый класс типа Трансфер, после чего в этом
классе беру их БД данные 2 аккаунтов, которые определяю случайным образом, сумму которую хочу перевести случайную, выполняю перевод и записываю изменения обратно в БД. При чем сумма всех средств на счетах должна оставаться начальной.
Описанный ниже подход работает есть потоков не больше 5-10 и потоки спят более чем по 500мс. Возможно у меня неправильный подход к решению проблемы. Относительно заботы с tryLock, пробовал, тоже самое получается, суммарно деньги то уходят в плюс, то в минус.
Плюсом ко всему, это чудо я пытаюсь сделать на Hibernate.
Что есть:
1. Класс какого-то аккаунта - Account (@Entity)
2. Класс унаследованный от потока, в котором я работаю непосредственно с run() - TransferThread extends Thread
3. Класс Transfer в котором происходит взаимодействие с аккаунтом у конкретного потока.
4. Класс Main
Собственно сейчас это выглядит так:
Класс Account (set/get я опустил чтобы не громоздить код):
public class Account {
private int id;
private int money;
public Account() {
}
public void widrawal(int sum) {
money += sum;
}
public void send(int sum) {
money -= sum;
}
}
Класс TransferThread (в нем, по задумке, нить отрабатывает транзакцию и засыпает на случайное время и так 10 раз):
public class TransferThread extends Thread {
Transfer transfer= new Transfer();
AtomicInteger atomicInteger = new AtomicInteger();
public void run() {
atomicInteger.set(10);
while (atomicInteger.get() > 0) {
//нить засыпает на случайное время
int a = (int) (Math.random() * (500 - 100)) + 100;
try {
transfer.transaction();
atomicInteger.getAndDecrement();
Thread.sleep(a);
} catch (InterruptedException e) {
}
}
}
}
Класс Transfer:
public class Transfer {
public void transaction() throws InterruptedException {
int sumSpis = (int) (Math.random() * 100) + 10;
int ranAccount1 = (int) (Math.random() * 50) + 1;
int ranAccount2 = (int) (Math.random() * 50) + 1;
Account a1 = null;
Account a2 = null;
a1 = HibernateSessionFactoryUtil.getSessionFactory().openSession().get(Account.class, ranAccount1);
a2 = HibernateSessionFactoryUtil.getSessionFactory().openSession().get(Account.class, ranAccount2);
int fromId = a1.getId();
int toId = a2.getId();
System.out.println("**************************************");
if (fromId < toId) {
synchronized (a1) {
synchronized (a2) {
transfer(a1, a2, sumSpis);
}
}
} else {
synchronized (a2) {
synchronized (a1) {
transfer(a1, a2, sumSpis);
}
}
}
System.out.println("сумма перевода: " + sumSpis);
System.out.println("денег на аккаунте: " + a1.getId() + " стало " + a1.getMoney() + " денег на аккаунте: " + a2.getId() + " стало " + a2.getMoney());
System.out.println("**************************************");
}
public void transfer(Account account1, Account account2, int sum) {
if (account1.getId() == account2.getId()) {
System.out.println("Одинаковые ИД");
return;
}
if (account1.getMoney() < sum) {
System.out.println("На аккаунте списания недостаточно средств денег");
return;
}
account1.widrawal(sum);
account2.send(sum);
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.update(account1);
session.update(account2);
tx.commit();
session.close();
}
}