ReentrantLock прошу разъяснить в чем может быть проблема.

 
 
 
Сообщения:15
День добрый господа, вопрос такого плана, разбираю многопоточность и связанные с ней проблемы.
На примере банковских транзакций пытаюсь отработать вариант с локерами.

Но толи я до конца не понял, как он должен работать, толи я понял но неправильно реализовал.

Суть в том, что у меня получается 1 поток получает один ресурс, второй получает второй ресурс. и получается опа, они получают ресурсы сикось накось.

1. Возможно стоит обернуть два акка в один клас и устанавливать лок на него
2. Пока поплыл.

Пока не могу понять вот что, как может лочится 1 акк в одном потоке и успеть залочится второй акк этого же потока.

Возможно в реальной жизни такого нет? и стоит после старта потока сделать некоторую задержку перед запуском второго потока? Но тогда чистота эксперемента не получится.
Хочу понять для себя , как залочить 2 акк в одном потоке, при условии, что потоки запускаются без промежутка времени.
Спасибо за понимание.

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    private static final long WAIT_SEC = 1;

    public static void main(String[] args) {

        Account a = new Account(1000);
        System.out.println(" Инициализация акк а");
        Account b = new Account(2000);
        System.out.println(" Инициализация акк б");


        //WITHOUT THREADS
        /*
        try {
            System.out.println(" Инициация перевода денег из а в b");
            transfer(a, b, 500);
            System.out.format(" Остаток на счету а: %s, остаток на счету b: %s%n", a.getBalance(), b.getBalance());
        } catch (InsuffisiendFunsException e) {
            System.out.println(" Недостаточно денег для совершения операции.");
        }

        try {
            System.out.println(" Инициация перевода денег из b в а");
            transfer(b, a, 300);
            System.out.format(" Остаток на счету а: %s, остаток на счету b: %s%n", a.getBalance(), b.getBalance());
        } catch (InsuffisiendFunsException e) {
            System.out.println(" Недостаточно денег для совершения операции.");
        }*/

        // using a Threads
        Thread thr1 = new Thread(new Runnable() {
            @Override
            public void run() {

                System.out.format(" Запуск потока: %s%n", 1);
                try {
                    System.out.format(" Инициализация перевода из а > b %n");
                    transfer(a, b, 500);
                    System.out.printf(" Остаток на счету а: %s, остаток на счету b: %s%n" , a.getBalance(), b.getBalance());
                    System.out.printf(" transfer из а > b successful. %n");
                } catch (InsuffisiendFunsException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thr1.start();


        // Второй поток
        Thread thr2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.format(" Запуск потока: %s%n", 2);
                try {
                    System.out.printf(" Инициализация перевода из b > a %n");
                    transfer(b, a, 300);
                    System.out.printf(" Остаток на счету а: %s, остаток на счету b: %s%n" , a.getBalance(), b.getBalance());
                } catch (InsuffisiendFunsException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf(" transfer b > a successful %n");
            }
        });
        thr2.start();
    }

    public static void transfer(Account a, Account b, int amount) throws InsuffisiendFunsException, InterruptedException {

        if(a.getBalance() < amount)
        {
            throw new  InsuffisiendFunsException();
        }

        /*
        synchronized (a)
        {
        //
            try {
                Thread.sleep(1000); // In this case , we have a deadlock. //event of deadlock
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (b)
            {
                a.withDraw(amount);
                b.deposit(amount);
            }
        }*/

        while (true) {

            // getting a first lock
            System.out.printf(" Getting first lock %s%n", a.getLock());
            if (a.getLock().tryLock(WAIT_SEC, TimeUnit.SECONDS)) {

                if(b.getLock().tryLock(WAIT_SEC, TimeUnit.SECONDS))
                {
                //Thread.sleep(50);
                System.out.printf(" Getting second lock %s%n", b.getLock());
               // if(b.getLock().tryLock(WAIT_SEC, TimeUnit.SECONDS)) {
                    try {
                        a.withDraw(amount);
                        b.deposit(amount);
                    } finally {
                        a.getLock().unlock();
                        b.getLock().unlock();
                        break;
                    }
                //}
                }
            }
        }


    }
}

class Account
{
    private int id = System.identityHashCode(this.hashCode());

    private final Lock lock = new ReentrantLock();

    private int balance;

    public Account(int initialisingBalance)
    {
        this.balance = initialisingBalance;
    }

    public void withDraw(int amount)
    {
        balance -= amount;
    }

    public void deposit(int amount)
    {
        balance += amount;
    }

    public int getBalance()
    {
        return this.balance;
    }

    public Lock getLock()
    {
        return lock;
    }
}

class InsuffisiendFunsException extends Exception{

}

Восхождение в джедая
 
 
Сообщения:15
Возможно нашел ответ.

Я для каждого акк формировал свой лок.
Немного переписал код, храню переменную лок, но создаю сам лок до конструирования акк, после передаю лок в конструктор акк и манипулирую получается одним локом на 2 акк.
Не знаю насколько это правильно, но лочатся оба акк и все выполняется как надо.
Вопрос заключается в конструкции
if(b.getLock().tryLock(..., ...)), если мы имеем ссылку на один локер, который поидее должен залочить сразу эти объекты, тогда второй объект он автоматом лочит и в этой конструкции нет надобности? Либо же он проверяет, какие объекты он не залочил и хранит в себе эти данные, тогда надо для каждого объекта вызывать лок?
Спасибо.

Восхождение в джедая
Изменен:10 ноя 2017 14:34
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет