Многопоточная программа Java - PullRequest
0 голосов
/ 24 февраля 2019

Моя задача - создать программу, которая будет иметь:

  • Класс Client
  • Класс Gate
  • Класс Museum

Client входит и покидает Museum, используя класс Gate.В музее может одновременно находиться не более 5 клиентов.

Когда я вводю, скажем, 1000 Clients, в какой-то момент вывод дает мне нежелательные числа.

Пример вывода:


Client (358) is leaving the Museum!   number of customers: 2
Client (214) is entering the Museum!   number of customers: 3
Client (214) is leaving the Museum!   number of customers: 2
Client (73) is entering the Museum!   number of customers: 5
Client (73) is leaving the Museum!   number of customers: 5
Client (397) is entering the Museum!   number of customers: 5
Client (76) is entering the Museum!   number of customers: 6
----------------------------------------------------------------
Client (930) is entering the Museum!   number of customers: 7
Client (930) is leaving the Museum!   number of customers: 6
Client (308) is entering the Museum!   number of customers: 6
Client (183) is entering the Museum!   number of customers: 6
Client (183) is leaving the Museum!   number of customers: 5
----------------------------------------------------------------
Client (647) is entering the Museum!   number of customers: 7
Client (647) is leaving the Museum!   number of customers: 6
----------------------------------------------------------------
Client (540) is entering the Museum!   number of customers: 7

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

Вот мой код:


Client.java

package client;

import gate.Gate;
import museum.Museum;

import java.util.Random;

public class Client extends Thread {
    private static int id = 0;
    private int clientID;

    public Client() {
        Client.id++;
        this.clientID = id;
    }

    @Override
    public void run() {
        this.enterMuseum();
        this.leaveMuseum();
    }

    ///////////////////////////////////////////////////////////////////////////////////

    private void enterMuseum() {
        try {
            Thread.sleep(new Random().nextInt(401) + 100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        while (true) {
            if (Gate.atomCustomer.get() < 5) {
                Museum.getGate(0).enter(this);
                break;
            }
        }
    }

    private void leaveMuseum() {
        Museum.getGate(1).exit(this);
    }

    public int getClientId() {
        return this.clientID;
    }

    ////////////////////////////////////////////////////////////////////////////////////
}

Gate.java

package gate;

import client.Client;

import java.util.concurrent.atomic.AtomicInteger;

public class Gate {
    public static AtomicInteger atomCustomer = new AtomicInteger();

    public Gate() {
    }

    public void enter(Client client) {
        if (atomCustomer.get() > 5) {
            System.out.println("----------------------------------------------------------------");
        }
        atomCustomer.incrementAndGet();

        System.out.println("Client (" + client.getClientId() + ") is entering the Museum!" +
                "   number of customers: " + atomCustomer.get());
    }

    public void exit(Client client) {
        atomCustomer.decrementAndGet();
        System.out.println("Client (" + client.getClientId() + ") is leaving the Museum!" +
                "   number of customers: " + atomCustomer.get());
    }
}

Museum.java

package museum;

import gate.Gate;

public class Museum {
    private static Gate[] gate = new Gate[2];

    public Museum() {
        gate[0] = new Gate();
        gate[1] = new Gate();
    }

    public static Gate getGate(final int numberOfGate) {
        return Museum.gate[numberOfGate];
    }
}

ApplicationTest.java

import client.Client;
import museum.Museum;

import java.util.ArrayList;
import java.util.Scanner;

public class ApplicationTest implements Runnable {
    private static int NUMBER_OF_CLIENTS = 0;
    private static ArrayList<Client> listOfClients;
    private static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        new Thread(new ApplicationTest()).start();
    }


    private static void init() {
        while (NUMBER_OF_CLIENTS < 5) {
            System.out.println("How many clients?( > 5): ");
            NUMBER_OF_CLIENTS = sc.nextInt();
        }

        listOfClients = new ArrayList<>(NUMBER_OF_CLIENTS);
        new Museum();

        for (int i = 0; i < NUMBER_OF_CLIENTS; i++) {
            ApplicationTest.listOfClients.add(new Client());
        }
        for (Client c : listOfClients) {
            c.start();
        }
    }


    @Override
    public void run() {
        ApplicationTest.init();
    }
}

Буду благодарен за любую помощь.Спасибо за ваше время.Q.

1 Ответ

0 голосов
/ 25 февраля 2019

Несколько предложений: не используйте общедоступные статические переменные и статические методы, такие как museum.getGate () или атомарный счетчик посетителей (это затрудняет понимание того, кто чем пользуется).Также класс Client должен быть полностью изолирован от логики «counter»;то есть клиент должен просто вызвать gate.enter (), а проверки доступа должны быть выполнены либо в Gate, либо в Museum.

Затем есть «критическая» часть, в которой вы пытаетесь назначить «разрешение»."для клиентов, в

 while (true) {
   if (Gate.atomCustomer.get() < 5) {
     //use museum.tryEnter() instead..
     Museum.getGate(0).enter(this);
     break;
   }
 }

Здесь, если два потока вызывают get () одновременно , они оба обнаружат, что количество клиентов, например, равно.4, и они оба войдут (проблема параллелизма).

Один из способов убедиться, что только один клиент получает разрешение - добавить вложенный вызов к некоторому синхронизированному методу, например

private synchronized boolean tryEnter() {
    if (counter<5) {
        counter++;
        return true;
    }
    else {
        return false;
    }
}

Нолучший способ назначить разрешения - использовать семафор (так что вам даже не нужен этот занятый цикл).https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...