Многопотоковый. Явные блокировки и условия

Мне трудно понять многопоточность. К сожалению, это одно из заданий, которое мне нужно выполнить, чтобы пройти курс.

Речь идет о поезде: - Поезд ждет, пока Пассажирский поток отправит несколько пассажиров, пока не будет достигнута вместимость.

  • Затем поезд отправляется в путь. В этот период пассажиры не могут сесть в поезд.

  • Следующий шаг — отключение, это процедура, вызываемая потоком Passenger.

  • Как только это происходит, цикл продолжается с остальными пассажирами.

У меня проблемы с разгрузкой, иногда я получаю исключение для массива за пределами границ.

Вот ошибка:

Пассажир 3 сел в поезд
Пассажир 0 сел в поезд
Пассажир 1 сел в поезд
Пассажир 12 сел в поезд
Пассажир 13 сел в поезд
ПОЕЗД ПОЛНЫЙ
МЕСТО: 0 Пассажир: 3
МЕСТО: 1 Пассажир: 0
МЕСТО: 2 Пассажир: 1
МЕСТО: 3 Пассажир: 12
МЕСТО: 4 Пассажир: 13
НАЧАЛО ПОЕЗДКИ
ПОЕЗДКА ЗАВЕРШАЕТСЯ
Пассажир 3 хочет сойти с поезда. МЕСТО: 0
Пассажиров: 3 сошли с поезда
Пассажиров осталось: 0
Пассажиров осталось: 1
Исключение в потоке "Thread-16" java.lang. ArrayIndexOutOfBoundsException: -1
в java.util.ArrayList.elementData(неизвестный источник)
в java.util.ArrayList.remove(неизвестный источник)
в parque.Train.unboardTrain(Train. java:104)
at parque.Passenger.run(Passenger.java:23)
Осталось пассажиров: 12
Осталось пассажиров: 13
Пассажир 15 хочет выйти из поезда. SEAT: -1 //Пассажира с ID 15 нет, да?

Я хотел бы знать, как я могу избежать этого исключения? Я подумал, может быть, реализовать другой замок отдельно от замка поезда, который будет отвечать за двери, или, может быть, это должно быть реализовано как условие? Помогите, пожалуйста.

Вот код:

public class Train extends Thread {
    private int id;
    private int capacity;
    private ArrayList<Integer> passengers;
    private Lock l = new ReentrantLock();
    private Condition trainFull = l.newCondition();
    private Condition boardTrain = l.newCondition();
    private Condition UnboardTrain = l.newCondition();
    private boolean canBoard = true;
    private boolean canUnboard = false;



//se definen los constructores  
    public Train(int id, int capacity) {
        this.id = id;
        this.capacity = capacity;
        this.passengers = new  ArrayList<Integer>(capacity);

    }//fin constructor  
    public Train(int id) {
        this.id = id;
        this.capacity = 5;
        passengers = new  ArrayList<Integer>(capacity);

    }//fin constructor

    public void boardTrain(int passengerId)  {
        l.lock();

        try{
            while(!canBoard)
                boardTrain.await();
                if (passengers.size() == capacity) {
                    canBoard = false;
                    trainFull.signal();
                } else {
                    passengers.add(passengerId);
                    System.out.println("Passenger " + passengerId +" has boarded the train");
                }//if

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("Exception at boarding");
        }finally{
            l.unlock();
        }//try


    }//fin subir

    public void waitsFullTrain() {     //waits until n (capacity) passengers board the train
        l.lock();

        try{
            trainFull.await();

            System.out.println("TRAIN FULL");
            for(int i = 0; i< passengers.size(); i++){
                System.out.println("            SEAT: " + i + " Passenger: " + passengers.get(i));
            }//for
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            l.unlock();
        }//try


    }//fin esperaLleno

    public void goForRide() throws InterruptedException{

        l.lock();
        try{
            System.out.println("RIDE STARTS");
            Thread.sleep(2000);
            System.out.println("RIDE ENDS");
            canUnboard = true;
            UnboardTrain.signalAll();       

        }finally{
            l.unlock();
        }       

    }//fin darVuelta


    public void unboardTrain(int pasajeroId) {
        l.lock();

        try{
            while(!canUnboard)
                UnboardTrain.await();
            //System.out.println("Bajando..");
            if(passengers.size() >0){
                System.out.println("Passenger "+ pasajeroId + " wants to get off the train. SEAT: "+passengers.indexOf(pasajeroId) );
                passengers.remove(passengers.indexOf(pasajeroId));
                System.out.println("    Passenger: " + pasajeroId + " off the train");

                for (int i = 0; i<passengers.size();i++){
                    System.out.println("            Passenger(s) left: "+passengers.get(i));
                }
            }else{
                System.out.println();
                canUnboard = false;
                canBoard = true;
                boardTrain.signalAll();         
            }//if
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("Exception at unboarding");
        }finally{
            l.unlock();
        }//try


    }//fin bajar



    public int id() {
        return id;
    }//fin id

    @Override
    public void run() {
        while(true){
            this.waitsFullTrain();
            try {
                this.goForRide();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }//fin while    

    }//fin run


}//fin clase


public class Passenger extends Thread{

    private int id;
    private Train t;

    public Passenger(int id, Train t) {
        this.id = id;
        this.t = t;
    }

    @Override
    public void run() {

        t.boardTrain(this.id);
        t.unboardTrain(this.id);

    }//run
}//Passenger


public class Main {

    public static void main(String[] args) {

        Train t = new Train(1);
        Passenger[] p = new Passenger[20];

        for (int i = 0; i < p.length; i++) {
            p[i]= new Passenger(i, t);
        }//for
        t.start();

        for (int i = 0; i < p.length; i++) {
            p[i].start();
        }//for

        try {
            t.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        for (int i = 0; i < p.length; i++) {
            try {
                p[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }//for
    }//main
}//clase

person remi.moncayo    schedule 12.08.2015    source источник
comment
Пожалуйста, опубликуйте трассировку стека.   -  person M. Shaw    schedule 12.08.2015
comment
Модель выглядит немного неправильно в этом дизайне для меня. Подумайте о реальном мире... Пассажиры не могут войти или выйти из поезда, если либо двери закрыты, либо поезд не стоит на станции (это должны быть ваши замки). Когда двери открыты, unboard(), board()... Когда истекает время ожидания, закрыть двери и поезд отправляется.   -  person Dave    schedule 12.08.2015
comment
Вы забыли задать вопрос! Очень важно, чтобы вы задавали реальный, конкретный вопрос. Вот почему кнопка, которую вы нажали, помечена Ask Question.   -  person David Schwartz    schedule 12.08.2015
comment
Спасибо, Дэйв, я попробую использовать твое предложение.   -  person remi.moncayo    schedule 12.08.2015
comment
Извините, Дэвид, это уже исправлено, ура   -  person remi.moncayo    schedule 12.08.2015


Ответы (2)


Проблема в том, что некоторые пассажиры пытаются выйти из поезда, хотя на самом деле они не в нем. Посмотрите на свой код в 'boardTrain' и включите следующее изменение для лучшего понимания вашей проблемы (см. новое сообщение, когда пассажир не смог сесть в поезд):

if (passengers.size() == capacity) {
    System.out.println("Passenger " + passengerId + " CANNOT board the train =>  TRAIN FULL");
    canBoard = false;
    trainFull.signal();
} else {
    passengers.add(passengerId);
    System.out.println("Passenger " + passengerId +" has boarded the train");
}

Теперь выполните свой код несколько раз. Когда вы снова получите ошибку, вы увидите, какой пассажир не попал в поезд (вы также можете увидеть это в своей трассировке, потому что нет никакого сообщения о том, что пассажир 15 сел в поезд).

Что происходит дальше? После посадки в поезд пассажир пытается выйти из него:

t.boardTrain(this.id);
t.unboardTrain(this.id);

но что произойдет, если пассажир, который не попал в поезд, попытается выйти из него? Вы получите свое сообщение, как:

Пассажир 15 хочет сойти с поезда. СИДЕНЬЕ: -1

а затем код продолжается и выполняет следующую строку:

passengers.remove(passengers.indexOf(pasajeroId));

и возникает исключение, потому что значение «passengers.indexOf(pasajeroId)» равно -1, и, как вы можете прочитать в документации, -1 не является допустимым индексом: http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#remove(int)

Попробуйте вернуть логическое значение с помощью метода «boardTrain» и снимите его с посадки только в том случае, если пассажир добрался до поезда.

Надеюсь, это поможет ;). Сюрте.

person BitExodus    schedule 12.08.2015
comment
Указанная вами ошибка верна, но решение должно заключаться в том, что пассажир должен подождать, пока он действительно не сможет сесть в поезд. - person xeed; 12.08.2015
comment
BitExodus большое спасибо за указание на это, я не подумал о тех пассажирах, которые не смогли сесть на поезд. К сожалению, я не могу изменить тип возврата, но постараюсь придумать способ, с помощью которого только те, кто сел в поезд, смогут выйти из него, спасибо! - person remi.moncayo; 12.08.2015
comment
@xeed, это вариант, я полагаю, это зависит от определения проблемы. Пассажир может подождать, чтобы сесть в поезд или покинуть станцию, кто знает. Справедливо. - person BitExodus; 13.08.2015

Итак, поиграв с этой вещью, вот правильная реализация на случай, если кому-то это может понадобиться:

В конце концов, хитрость заключалась в том, чтобы проверить, полон ли поезд, СРАЗУ ПОСЛЕ посадки пассажира. Я разрешал следующему пассажиру использовать метод boardTrain, затем проверял вместимость, если она заполнена, отклонял этого пассажира, но этот пассажир «потерялся», поэтому у меня была небольшая проблема в методе unboard и ArrayIndexOutOfBoundsException: -1.

public class Train extends Thread {
    private int id;
    private int capacity;
    private ArrayList<Integer> passengers;
    private Lock l = new ReentrantLock();
    private Condition trainFull = l.newCondition();
    private Condition boardTrain = l.newCondition();
    private Condition unboardTrain = l.newCondition();
    private boolean canBoard = true;
    private boolean canUnboard = false;



//se definen los constructores  
    public Train(int id, int capacity) {
        this.id = id;
        this.capacity = capacity;
        this.passengers = new  ArrayList<Integer>(capacity);

    }//fin constructor  
    public Train(int id) {
        this.id = id;
        this.capacity = 5;
        passengers = new  ArrayList<Integer>(capacity);

    }//fin constructor

    public void boardTrain(int passengerId)  {
        l.lock();

        try{
            while(!canBoard)
                boardTrain.await();
            passengers.add(passengerId);
            System.out.println("Passenger: " + passengerId +" has boarded the train");

            if (passengers.size() == capacity) {  //<------this here did the trick!
                canBoard = false;
                trainFull.signalAll();
            }


        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("Exception at boarding");
        }finally{
            l.unlock();
        }//try


    }//fin subir

    public void waitsFullTrain() {     //waits until n (capacity) passengers board the train
        l.lock();

        try{
            trainFull.await();
                        System.out.println("TRAIN FULL");
            for(int i = 0; i< passengers.size(); i++){
                System.out.println("            SEAT: " + i + " Passenger: " + passengers.get(i));
            }//for
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            l.unlock();
        }//try


    }//fin esperaLleno

    public void goForRide() throws InterruptedException{

        l.lock();
        try{
            System.out.println("RIDE STARTS");
            Thread.sleep(2000);
            System.out.println("RIDE ENDS");
            canUnboard = true;
            unboardTrain.signal();      

        }finally{
            l.unlock();
        }       

    }//fin darVuelta


    public void unboardTrain(int pasajeroId) {
        l.lock();

        try{
            while(!canUnboard)
                unboardTrain.await();
            //System.out.println("Bajando..");

            if(passengers.size() >0){
                if(passengers.indexOf(pasajeroId) > -1){
                    System.out.println("Passenger "+ pasajeroId + " wants to get off the train. SEAT: "+passengers.indexOf(pasajeroId) );
                    passengers.remove(passengers.indexOf(pasajeroId));
                    System.out.println("    Passenger: " + pasajeroId + " off the train");
                    if(passengers.size() ==0){
                        System.out.println();
                        canUnboard = false;
                        canBoard = true;
                        boardTrain.signalAll();                         
                    }else{
                        unboardTrain.signal();
                        System.out.print("      Remaining passengers: ");
                        for (int i = 0; i<passengers.size();i++){
                            System.out.print(" "+passengers.get(i));
                        }//for
                    }


                }else{
                    unboardTrain.signal();
                }//if
            }else{
                System.out.println();
                canUnboard = false;
                canBoard = true;
                boardTrain.signalAll();         
            }//if
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("Exception at unboarding");
        }finally{
            l.unlock();
        }//try


    }//fin bajar



    public int id() {
        return id;
    }//fin id


    public void run() {
        while(true){
            this.waitsFullTrain();
            try {
                this.goForRide();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }//fin while    

    }//fin run


}//fin clase
person remi.moncayo    schedule 28.08.2015