J
J
Jake Taylor2020-06-28 18:07:49
Java
Jake Taylor, 2020-06-28 18:07:49

What is wrong with using wait() and notifyAll() when multithreading in Java?

What's wrong with the ship program?

The ShipGenerator class creates a random number of ships. The class has a getShip() method that returns the generated ships in sequence.

source

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)];
    }
}



There is a class called Wharf , which is a thread that takes over a ship and loads it with goods by calling the add() method of the Ship class .
Ship class

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 class.

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

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 class

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)];
    }
}



And, in fact, the Main class itself, in which 3 (flows) berths are created ( Berth class ), into which ships should call in turn. Only one ship can be in one berth at a time.
class 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);
    }
}



At the output we have:
Console

/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)



Ships and moorings are being built. Everything is fine here.
The getShip() method is synchronized across threads.
Q: Why are the ships not loading?

Tried that, and it still doesn't work.
source

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;
    }



It turns out that only the same ship comes up all the time (the very first one). And so endlessly. Here is the console:
Console

# 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)

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question