Задать вопрос
@n199a
Java

Что не так с использованием wait() и notifyAll() при многопоточности в Java?

Что не так в программе с кораблями?

В классе ShipGenerator создается рандомное число кораблей. В классе имеется метод getShip(), который последовательно возвращает созданные корабли.
Исходник

package Ship;

import Ship.types.SizeShip;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ShipGenerator {
    private final int MAX_SHIPS;
    private final List<Ship> ships = new ArrayList<Ship>();

    public ShipGenerator(){
        MAX_SHIPS = 5 + (int)(Math.random() * 5);
        createShips();
    }

    public synchronized Ship getShip(){

        while (){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            for(Ship ship : ships){
                System.out.println("Корабль: " + ship.getName() + " убыл из БД кораблей!");
                return ship;
            }

            notifyAll();
        }

        return null;
    }


    private void createShips(){
        // создаем рандомное количество кораблей в диапазоне
        for(int i = 0; i < MAX_SHIPS; i++) {
            Ship ship = new Ship(getRandomSizeShip());
            ships.add(ship);
        }
    }

    private SizeShip getRandomSizeShip(){
        Random random = new Random();
        return SizeShip.values()[random.nextInt(SizeShip.values().length)];
    }
}



Имеется класс Причал, который является потоком, который принимает на себя корабль и загружает его товаром с помощью вызова метода add() класса Ship.
класс Ship

package Ship;

/**
 * Класс - Ship (корабль).
 */

import Ship.types.SizeShip;

public class Ship {
    {id++;}

    private static int id;                  // идентификационный номер
    private final String NAME;
    private final SizeShip size;
    private int nowGoods = 0;               // текущее количество товара

    public Ship(SizeShip size){
        this.size = size;
        this.NAME = "# Ship " + id;
        System.out.println("\n" + this.NAME + " was created!" +
                "\nSize: " + size.toString() +
                "\nCarrying capacity: " + size.getValue() + " (units)");
    }

    public boolean isFull(){
        if(nowGoods < size.getValue())
            return false;
        else {
            System.out.println("[FULL] " + this.NAME + " is fully loaded at " + nowGoods + " (pcs.)");
            return true;
        }
    }

    public void add(int count){
        this.nowGoods += count;
    }

    public int getNowGoods(){
        return this.nowGoods;
    }

    public String getName(){
        return this.NAME;
    }

}



Класс Berth.

/**
 *  Причал
 */

public class Berth implements Runnable {
    {id++;}

    private static int id;
    private final String NAME;
    private final ShipGenerator generator;
    private final SizeBerth size;


    public Berth(ShipGenerator generator){
        this.NAME = "# Berth " + id;
        this.generator = generator;
        this.size = getRandomSizeBerth();
        System.out.println("\n" + this.NAME + " was created!" +
                "\nSize: " + size.toString() +
                "\nCarrying capacity: " + size.getValue() + " (units)");
        new Thread(this, NAME).start();
    }

    @Override
    public void run() {
        //while (true){
            try {
                Ship ship = generator.getShip();
                System.out.println("\n" + ship.getName() + " arrived at the " + this.NAME);

                while (!ship.isFull()){
                    ship.add(size.getValue());
                    System.out.println(ship.getName() + " (" + this.NAME + ") " + " loaded at " + ship.getNowGoods() + " (psc.)");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        //}
    }

    private SizeBerth getRandomSizeBerth(){
        Random random = new Random();
        return SizeBerth.values()[random.nextInt(SizeShip.values().length - 1)];
    }

}



класс ShipGenerator

public class ShipGenerator {
    private final int MAX_SHIPS;
    private final List<Ship> ships = new ArrayList<Ship>();

    public ShipGenerator(){
        MAX_SHIPS = 5 + (int)(Math.random() * 5);
        createShips();
    }

    public synchronized Ship getShip(){

        while (!ships.isEmpty()){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            for(Ship ship : ships){
                ships.remove(ship);
                System.out.println("Корабль: " + ship.getName() + " убыл из БД кораблей!");
                return ship;
            }

            notifyAll();
        }

        return null;
    }


    private void createShips(){
        // создаем рандомное количество кораблей в диапазоне
        for(int i = 0; i < MAX_SHIPS; i++) {
            Ship ship = new Ship(getRandomSizeShip());
            ships.add(ship);
        }
    }

    private SizeShip getRandomSizeShip(){
        Random random = new Random();
        return SizeShip.values()[random.nextInt(SizeShip.values().length)];
    }
}



И, собственно, сам класс Main, в которос создается 3 (потока) причала (класс Berth), в которые должы по очереди заезжать корабли. В одном причале одновременно может находиться только один корабль.
класс Main

public class Main {

    public static void main(String[] args) {

        ShipGenerator shipGenerator = new ShipGenerator();

        Berth berth1 = new Berth(shipGenerator);
        Berth berth2 = new Berth(shipGenerator);
        Berth berth3 = new Berth(shipGenerator);
    }
}



На выходе имеем:
Консоль

/usr/lib/jvm/java-1.11.0-openjdk-amd64/bin/java -javaagent:/snap/intellij-idea-ultimate/228/lib/idea_rt.jar=37661:/snap/intellij-idea-ultimate/228/bin -Dfile.encoding=UTF-8 -classpath /home/n199a/Документы/java/prj/6.4/out/production/6.4 com.company.Main

# Ship 1 was created!
Size: SMALL
Carrying capacity: 20 (units)

# Ship 2 was created!
Size: LARGE
Carrying capacity: 40 (units)

# Ship 3 was created!
Size: MEDIUM
Carrying capacity: 30 (units)

# Ship 4 was created!
Size: SMALL
Carrying capacity: 20 (units)

# Ship 5 was created!
Size: LARGE
Carrying capacity: 40 (units)

# Ship 6 was created!
Size: LARGE
Carrying capacity: 40 (units)

# Ship 7 was created!
Size: LARGE
Carrying capacity: 40 (units)

# Ship 8 was created!
Size: LARGE
Carrying capacity: 40 (units)

# Ship 9 was created!
Size: MEDIUM
Carrying capacity: 30 (units)

# Berth 1 was created!
Size: SMALL
Carrying capacity: 5 (units)

# Berth 2 was created!
Size: MEDIUM
Carrying capacity: 10 (units)

# Berth 3 was created!
Size: SMALL
Carrying capacity: 5 (units)



Создаются корабли и причалы. Тут всё нормально.
Метод getShip() синхронизирован для потоков.
Вопрос: почему корабли не загружаются?

Пробовал и так, всё равно не получается.
Исходник

public synchronized Ship getShip(){
        try {
            notifyAll();
            for(Ship ship : ships){
                System.out.println("Корабль: " + ship.getName() + " убыл из БД кораблей!");
                return ship;
            }
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }



Получается, что выплывает только один и тот же корабль всё время (самый первый). И так бесконечно. Вот консоль:
Консоль

# Ship 1 arrived at the # Berth 1
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
[1]Корабль: # Ship 1 убыл из БД кораблей!

# Ship 1 arrived at the # Berth 3
[FULL] # Ship 1 is fully loaded at 40 (pcs.)

# Ship 1 arrived at the # Berth 2
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
[1]Корабль: # Ship 1 убыл из БД кораблей!

# Ship 1 arrived at the # Berth 3
[1]Корабль: # Ship 1 убыл из БД кораблей!
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
[1]Корабль: # Ship 1 убыл из БД кораблей!

# Ship 1 arrived at the # Berth 2
[FULL] # Ship 1 is fully loaded at 40 (pcs.)

# Ship 1 arrived at the # Berth 1
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
[1]Корабль: # Ship 1 убыл из БД кораблей!

# Ship 1 arrived at the # Berth 3
[1]Корабль: # Ship 1 убыл из БД кораблей!
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
[1]Корабль: # Ship 1 убыл из БД кораблей!

# Ship 1 arrived at the # Berth 2
[FULL] # Ship 1 is fully loaded at 40 (pcs.)
Process finished with exit code 130 (interrupted by signal 2: SIGINT)

  • Вопрос задан
  • 158 просмотров
Подписаться 1 Средний 1 комментарий
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы