Многопоточность. Задача - заправная станция

Рейтинг: -2Ответов: 1Опубликовано: 08.01.2023

Прохожу многопоточность , помогите в решении такой задачи. Заправная станция заправляет 3 автомобиля. У станции есть общее количество литров . Запрка авто - это поток в котором указывается какая машина и на сколько литров заправляется, время на запраку(от 3 - 10 секунд) и сообщение об остатке литров на станциию. Как сделать так, чтобы после завершения каждого потока уменьшалось общее количество литров на станции. Сейчас при каждой заправке литры отнимаются один раз от общего количества топлива на станции - такой вывод :

 Starting refuel Car 1 - 50 liters
Petrol station remainder - 450 liters
Starting refuel Car 3 - 70 liters
Petrol station remainder - 430 liters
Starting refuel Car 2 - 60 liters
Petrol station remainder - 440 liters

вот мой код :

public class PetrolStation implements Runnable{
private volatile int amount = 500 ;
private int fuel;
private ReentrantLock locker;

public PetrolStation(int fuel, ReentrantLock locker) {
    this.fuel = fuel;
    this.locker = locker;
}

@Override
public void run() {
    locker.lock();
    System.out.println("Starting refuel " + Thread.currentThread().getName() + fuel + " liters");
    try {
        int duration = getRandomInRange(3000, 10000);
        Thread.sleep(duration);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println("Petrol station remainder - " + doRefuel() + " liters");
    locker.unlock();
}
private int doRefuel(){
    return amount - fuel;
}

public static int getRandomInRange(int begin, int end) {
    double random = Math.random();
    return (int) (random * (end - begin + 1)) + begin;
}

}

И мейн :

public class MainPetrolStation {
private static final int SIZE = 3;
private static final ReentrantLock locker = new ReentrantLock();

public static void main(String[] args) throws InterruptedException {
    Thread[] thread = new Thread[SIZE];
    thread[0] = new Thread(new PetrolStation(50, locker), "Car 1 - ");
    thread[1] = new Thread(new PetrolStation(60, locker), "Car 2 - ");
    thread[2] = new Thread(new PetrolStation(70, locker), "Car 3 - ");
    for (int i = 0; i < SIZE; i++) {
        thread[i].start();
    }
}

Ответы

▲ 3Принят

В целом ваша проблема никак не связана с многопоточностью. Ваша переменная amount является нестатическим полем класса MainPetrolStation. Посему для каждого экземпляра класса она будет своя. Вы создаете 3 экземпляра класса MainPetrolStation , для каждого из них переменная amount равна 500, т.е. по сути это три разные переменные amount, они никак не пересекаются и никакая синхронизация и в целом многопоточность здесь вообще не причем. Ну и венец всего - ваш метод doRefuel(), где вы делаете так: return amount - fuel; каким образом это вообще должно повлиять на переменную amount (даже если бы она была единственная для трех экземпляров класса) вообще непонятно. вы просто посчитали разницу и вернули результат (и это никак не повлияло ни на какие переменные и поля, результат расчетов доступен только как результат, возвращающийся из метода).

Я не знаю, чего именно вы хотите добиться от этого примера. На мой взгляд он в принципе неудачный и не очень хорошо демонстрирует многопоточность. Тем более не совсем понятно, что именно вы ожидаете от ReentrantLock, потому как, если вы понимаете, что делает synchronized, то и ReentrantLock трудностей не вызовет, ставить с ним "эксперименты" особого смысла нет, а если не понимаете, то вам нужно разобраться с synchronized. Но все же, если вы думаете, что вам это поможет, то в контексте вашей задачи решение выглядит примерно так (хотя с точки зрения реализации оно нелепое, но я стараюсь максимально сохранить вашу задачу в исходном виде):

import java.util.concurrent.locks.ReentrantLock;

public class MainPetrolStation {
    
    private static final ReentrantLock locker = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        PetrolStationAmount petrolStationAmount = new PetrolStationAmount(500);
        Thread[] threads = {
            new Thread(new PetrolStation(petrolStationAmount, 50, locker), "Car 1 - "),
            new Thread(new PetrolStation(petrolStationAmount, 60, locker), "Car 2 - "),
            new Thread(new PetrolStation(petrolStationAmount, 70, locker), "Car 3 - ")};
        for (Thread thread : threads) thread.start();
    }
}

class PetrolStationAmount{
    
    private int amount;

    public PetrolStationAmount(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
    
}

class PetrolStation implements Runnable {
    
    private final PetrolStationAmount petrolStationAmount;
    private final int fuel;
    private final ReentrantLock locker;

    public PetrolStation(PetrolStationAmount petrolStationAmount, int fuel, ReentrantLock locker) {
        this.petrolStationAmount = petrolStationAmount;
        this.fuel = fuel;
        this.locker = locker;
    }

    @Override
    public void run() {
        locker.lock();
        System.out.println("Starting refuel " + Thread.currentThread().getName() + fuel + " liters");
        try {
            int duration = getRandomInRange(3000, 10000);
            Thread.sleep(duration);
            System.out.println("Petrol station remainder - " + doRefuel() + " liters");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            locker.unlock();
        }
    }

    private int doRefuel() {
        petrolStationAmount.setAmount(petrolStationAmount.getAmount() - fuel);
        return petrolStationAmount.getAmount();
    }

    public static int getRandomInRange(int begin, int end) {
        double random = Math.random();
        return (int) (random * (end - begin + 1)) + begin;
    }

}

p.s. запомните, что lock - unlock для класса ReentrantLock ВСЕГДА(!) должен быть использован исключительно вместе с блоком finally (как у меня в примере). Об этом говориться в документации, в любых книгах , примерах и даже любая среда разработки такое должна подсказать.