Страницы

Поиск по вопросам

четверг, 11 апреля 2019 г.

Как правильно сделать синхронизацию?

Практикуюсь работать с синхронизацией потоков и не получается сделать так, как придумал. У меня есть аккаунт в банке, муж и жена кладут туда деньги. Я хочу сделать так, чтоб они по очереди это делали. Я делаю это так:
public class Main { public static void main(String[] args) { new Wife().start(); new Husband().start(); } }
public class Husband extends Thread{ @Override public void run() { super.run(); for (int i = 0; i < 10; i++) { System.out.println("Husband " + i + " " + Account.deposit()); } } }
public class Wife extends Thread{ @Override public void run() { super.run(); for (int i = 0; i < 10; i++) { System.out.println("Wife " + i + " " + Account.deposit()); } } }
public class Account { public synchronized static int deposit(){ account += 300; return account; } }
Но все равно получается бардак. Может вместо synchronized нужно семафоры использовать? Чтоб была правильная очередность.


Ответ

Сейчас нет возможности обдумать элегантное решение, но я бы приоритеты задавал. Вот решение в лоб:
public class App { public static void main(String[] args) { Account account = new Account(0); new Wife(account).start(); new Husband(account).start(); }
public static abstract class FamilyMember extends Thread {
protected Account mAccount; protected String mId;
public FamilyMember(Account account) { mAccount = account; }
@Override public void run() { super.run(); startMe(); }
protected abstract void startMe(); }
public static class Husband extends FamilyMember {
public Husband(Account account) { super(account); mId = getClass().getName(); mAccount.registerMember(getClass().getName()); }
@Override protected void startMe(){ for (int i = 0; i < 10; i++) { try { while(!mAccount.CheckPriority(mId)) Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Husband " + i + " " + mAccount.deposit(mId)); } } }
public static class Wife extends FamilyMember {
public Wife(Account account) { super(account); mId = getClass().getName(); mAccount.registerMember(getClass().getName()); }
@Override protected void startMe(){ for (int i = 0; i < 10; i++) { try { while(!mAccount.CheckPriority(mId)) Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Wife " + i + " " + mAccount.deposit(mId)); } } }
public static class Account {
private int mAccount = 0;
private List mMembers = new ArrayList();
/** * проверяем имеет ли член семьи максимальный приоритет * @param id * @return */ public synchronized boolean CheckPriority(String id){ Member member = mMembers.stream().max(Comparator.comparingInt(Member::getPriority)).get(); return member.getId().equals(id); }
public void registerMember(String id){ mMembers.add(new Member(id, 0)); }
public Account(int account) { mAccount = account; }
public synchronized int deposit(String id) { mAccount += 300; // понижаем приоритет mMembers.stream().filter(member -> member.mId.equals(id)).findFirst().get().decPriority(); return mAccount; }
public static class Member{
public String mId; public int mPriority;
public Member(String id, int priority){ mId = id; mPriority = priority; }
public void decPriority(){ mPriority--; }
public int getPriority(){ return mPriority; }
public String getId(){ return mId; } } } }
Выведет:
Wife 0 300 Husband 0 600 Wife 1 900 Husband 1 1200 Wife 2 1500 Husband 2 1800 Wife 3 2100 Husband 3 2400 Wife 4 2700 Husband 4 3000 Wife 5 3300 Husband 5 3600 Wife 6 3900 Husband 6 4200 Wife 7 4500 Husband 7 4800 Wife 8 5100 Husband 8 5400 Wife 9 5700 Husband 9 6000
Потоки, перед тем как внести деньги на счёт, проверяют свой приоритет с помощью CheckPriority. Если текущий поток имеет больший приоритет, то вносятся деньги mAccount.deposit(mId) и понижается текущий приоритет
mMembers.stream().filter(member -> member.mId.equals(id)).findFirst().get().decPriority();
Иначе засыпает на 1мс.

Комментариев нет:

Отправить комментарий