Многопоточность, ожидание освобождение потока
подскажите пожалуйста как быть, разбираюсь с многопоточностью, в данный момент есть одна полоса, и два самолета, взлет происходит тогда, когда самолет прогрелся и взлетел один из них, сейчас хочу сделать так, чтобы было две полосы и три самолета, т.е два самолета прогрелись и взлетели, а другой ждал пока освободится какая-либо из полос?
public class Main {
public static void main(String[] args) throws InterruptedException {
final int min = 1000;
final int max = 10000;
final int rnd = DelayTakeoff.rnd(min,max);
Band newBand = new Band();
Passenger passenger = new Passenger("Пассажирский");
Cargo cargo = new Cargo("Грузовой");
newBand.addPlane(passenger);
newBand.addPlane(cargo);
DelayTakeoff thread1 = new DelayTakeoff(passenger.name, passenger,newBand,5000,rnd);
DelayTakeoff thread2 = new DelayTakeoff(cargo.name, cargo,newBand,10000,rnd);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
public class DelayTakeoff extends Thread {
private final Band object;
private final int delay;
private final int rndDelay;
DelayTakeoff(String name,Plane plane, Band object, int delay, int rndDelay) {
this.setName(name);
this.object = object;
this.delay = delay;
this.rndDelay = rndDelay;
}
public int getDelay() {
return delay;
}
public int getRndDelay() {
return rndDelay;
}
public static int rnd(int min, int max) {
max -=min;
return (int) (Math.random() * ++ max) + min;
}
@Override
public void run() {
try {
System.out.println("Прогрев начался: " + getName());
Thread.sleep(getRndDelay());
System.out.println("Прогрев завершился: " + getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object) {
try {
System.out.println("Взлетает: " + getName());
Thread.sleep(getDelay());
System.out.println("Взлетел: " + getName());
} catch (InterruptedException e1) {}
object.notify();
try {
object.wait();
} catch (InterruptedException e2) {}
}
}
}
Реализовал такое решение, но мне кажется оно не верно, но вывод соответсвует действительности.
public class Main {
public static void main(String[] args) throws InterruptedException {
final int min = 1000;
final int max = 10000;
final int rnd = DelayTakeoff.rnd(min,max);
Band newBand = new Band();
Band newBandTwo = new Band();
Passenger passenger = new Passenger("Пассажирский");
Cargo cargo = new Cargo("Грузовой");
Cargo cargo1 = new Cargo("222");
newBand.addPlane(passenger);
newBandTwo.addPlane(cargo);
newBandTwo.addPlane(cargo1);
DelayTakeoff thread1 = new DelayTakeoff(passenger.name, passenger,newBand,5000,rnd);
DelayTakeoff thread2 = new DelayTakeoff(cargo.name, cargo,newBandTwo,10000,rnd);
DelayTakeoff thread3 = new DelayTakeoff(cargo1.name, cargo, newBand, 2000, rnd);
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
}
}
Вывод:
Прогрев начался: Пассажирский
Прогрев начался: Грузовой
Прогрев начался: 222
Прогрев завершился: Грузовой
Прогрев завершился: Пассажирский
Прогрев завершился: 222
Взлетает: Грузовой
Взлетает: 222
Взлетел: 222
Взлетает: Пассажирский
Взлетел: Пассажирский
Взлетел: Грузовой
Ответы (1 шт):
Каждая из полос - это независимый разделяемый ресурс, за который конкурируют самолеты в попытке взлететь.
При этом попытки занять этот ресурс не должны приводить к непроизводительному простою самолета в ожидании (вдруг другая полоса в это время освободится).
Для управления разделяемыми ресурсами создадим контейнер, который выдаст "наружу" только необходимый минимум, позволяющий (в рамках данной задачи) занять полосу и освободить её:
class Airport { // это и есть наш контейнер
private final List<Band> bands = new ArrayList<>(); // все полосы аэропорта
Airport(int bandCount) { // создаем в конструкторе и распоряжаемся сами
for (int i = 0; i < bandCount; i++) {
bands.add(new Band("band " + i));
}
}
@Nullable
public Band tryLockBand() {
Band result = null;
for (Band band : bands) {
if (band.tryLock()) {
result = band;
break;
}
}
if (result != null) System.out.println("Полоса " + result.name + " зарезервирована");
return result;
}
public void unlockBand(@NotNull Band band) {
band.unlock();
System.out.println("Полоса " + band.name + " освобождена");
}
static class Band {
public final String name;
private final Lock lock = new ReentrantLock(); // полоса "сама" следит за возможностью её занять.
Band(String name) {this.name = name;}
private boolean tryLock() {return lock.tryLock();}
private void unlock() {lock.unlock();}
}
}
Комментарии для класса Airport, думаю, излишни - два основных метода tryLockBand и unlockBand отвечают за получение (первой из свободных) и высвобождение полосы. Сами полосы выступают неотъемлимой частью аэропорта, поэтому описаны как вложенный класс (по большому счёту - правильно было бы убрать из описания Band модификатор static, но тогда вполне справедливо появится hint).
Что касаемо самолета - он не может заранее знать, какая из полос ему станет доступна, поэтому зарезервировать полосу под себя он будет пытаться через аэропорт:
class Plane extends Thread {
public final String name;
private final Airport airport;
Plane(String name, Airport airport) {
this.name = name;
this.airport = airport;
}
@Override
public void run() {
try {
System.out.println("Прогрев начался: " + name);
Thread.sleep((long) (Math.random() * 1000) + 500);
System.out.println("Прогрев завершился: " + name);
System.out.println("Резервируем полосу для: " + name);
Airport.Band band;
do {
band = airport.tryLockBand();
Thread.sleep(100);
} while (band == null);
// now we get band. let`s take off!!!
try {
System.out.println("Взлетает: " + name + " с полосы " + band.name);
Thread.sleep((long) (Math.random() * 1000) + 500);
System.out.println("Взлетел: " + name);
} finally {
airport.unlockBand(band);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
По большому счёту - комментарии тут тоже излишни, вывод в консоль выполняет эту задачу.
Ну и, напоследок - "основной" код:
public class Main {
public static void main(String[] args) {
Airport airport = new Airport(2); // у аэропорта будет 2 полосы
List<Plane> planes = new ArrayList<>();
for (int i = 0; i < 15; i++) { // пусть пытаются взлететь 15 самолетов
planes.add(new Plane("Plane " + i, airport));
}
planes.forEach(Thread::start); // стартуем работу наших независимых самолетов
planes.forEach(plane -> {
try {
plane.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}); // и подождем, пока они все не взлетят.
}
}
P.S. на самом деле - этот код не совсем (а, вернее, совсем не) отображает реальность: не самолёт (борт) должен выбирать полосу, а аэропорт по готовности борта и с учетом метеоусловий и других параметров выделяет ему полосу.