RMI Produzent-Verbraucher-Problem - Code review

cspecial

Mitglied
Hallo. Ich möchte verteilte Anwendungen reservieren, wobei die Clienten als Konsumenten, die auf einen serverseitigen Puffer zugreifen, auftreten. Realisiert wird die Kommunikation über die RMI-Schnittstelle.
Der Server besitzt zudem einen Produzent, der den Puffer füllt. Die maximale Puffergröße ist 20.

Die Aufgabe habe ich mir selbst zusammengestellt und sollte nur dazu dienen, den Wissensstand zu überprüfen. Bitte die Ausführungen der Ausnahmebehandlungen, wie sie im Code auftreten, unberücksichtigt lassen und voraussetzen, dass die Objekte serverseitig registriert wurden.

Vielen Dank im Voraus!

Java:
public class Buffer

{

   private Vector<Integer> buffer;

   private Semaphor mutex;

   private boolean flag1;

   private boolean flag2;   


   public Buffer()

   {

      buffer = new Vector<Integer> buffer;

      mutex = new Semaphor(1);

      flag1 = false;

      flag2 = false;

   }


   public synchronized Integer get()

   {

      if (buffer.size() == 0)

      {

         flag1 = true;

 

         if(flag2)

         {

            notify();

         }


         try

         {

            wait();

         } catch(Exception e)

         {}

       }


       mutex.p();

       Integer temp = buffer.lastElement();

       buffer.remove(temp);

       mutex.v();

 

       return temp;

    }


    public synchronized void put(Integer a)

   {

      if (buffer.size() == 20)

      {

         flag2 = true;

 

         if(flag1)

         {

            notify();

         }


         try

         {

            wait();

         } catch(Exception e)

         {}

       }


       mutex.p();

       buffer.add(a);

       mutex.v();

    }

}

          



public interface Consumer extends Remote

{

   public Integer get() throws RemoteException;

}




public class ConsumerImpl extends UnicastRemoteObject implements Consumer

{

   private Buffer buffer;

  

   public ConsumerImpl(Buffer buff) throws RemoteException

   {

      buffer = buff;

   }


   public Integer get() throws RemoteException

   {

      return buffer.get();

   }

}




public class Producer extends Thread

{

   private Buffer buffer;


   public Producer(Buffer buff)

   {

      buffer = buff;

      this.start();

   }


   public void run()

   {

      Integer a = 0;

 

      while (true)

      {

         buffer.put(a);

 

         try

         {

            sleep(1000);

         }

         catch (Exception e) {}

      }

   }

}




public class Server

{

   public static void main(String[] args)

   {

      try

      {

         Buffer buffer = new Buffer();

         ConsumerImpl c1 = new ConsumerImpl(buffer);

         Producer prod = new Producer(buffer);

         Naming.rebind("Server1", c1);

       }

       catch(Exception e)

       {

          return;

       }

   }

}




public class Client_J

{

   public static void main(String[] args)

   {

      try

      {

         Consumer c1 = (Consumer) Naming.lookup("rmi://" + args[0] +   "/Server1");


         while(true)

         {

            System.out.println(c1.get());

         }

       }

       catch(Exception e)

       {

           return;

       }

   }

}
 

mihe7

Top Contributor
Habe mir jetzt nur die Buffer-Klasse überflogen: schlecht wäre auch nicht, wenn flag1 und flag2 vernünftige Namen hätten und man wissen würde, welche Klassen Du verwendest (wo kommt denn Semaphor her? Ist Vector java.util.Vector?)
 

cspecial

Mitglied
Habe mir jetzt nur die Buffer-Klasse überflogen: schlecht wäre auch nicht, wenn flag1 und flag2 vernünftige Namen hätten und man wissen würde, welche Klassen Du verwendest (wo kommt denn Semaphor her? Ist Vector java.util.Vector?)
Genau, java.util.Vector.

Sempahor:
Code:
public class Semaphor
{
    private int value;

    public Semaphor(int val)
    {
        value = val;
    }

    public synchronized void p()
    {
        while (value < 0)
        {
            try
            {
                wait();
            }
            catch (Exception e) {}
        }

        value--;
    }
 
 
    public synchronized void v()
    {
        if (value < 0)
        {
            notifyAll(); //bzw. notify()
        }
     
        value++;
    }
}

Edit: flag1 wird gesetzt, sobald mindestens ein Konsument im Blockiert-Zustand ist (Puffer leer). Das flag2 wird gesetzt, sobald der Produzent blockiert wird (Puffer voll).

Edit2: Habe vergessen, die Flags zu entfernen (Anpassung der beiden Methoden in der Buffer-Klasse)
Code:
public synchronized Integer get()
   {
      while (buffer.size() == 0)
      {
         flag1 = true;

         if(flag2)
         {
            notify();
            flag2 = false;
         }

         try
         {
            wait();
         } catch(Exception e) {}

       }

       mutex.p();
       Integer temp = buffer.lastElement();
       buffer.remove(temp);
       mutex.v();

       return temp;
    }


    public synchronized void put(Integer a)
   {

      if (buffer.size() == 20)
      {
         flag2 = true;
        
         if(flag1)
         {
            notifyAll();
            flag1 = false;
         }

         try
         {
            wait();
         } catch(Exception e)         {}
       }

       mutex.p();
       buffer.add(a);
       mutex.v();
    }
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Noch/Nur zu Buffer:

- flag1 und flag2 bleiben immer true (oder ich überseh das setzen auf false).
- die beiden sollten bessere Namen bekommen...
- buffer = new Vector<Integer> buffer; sollte vermutlich eher buffer = new Vector<>(20); sein?
- p() und v() wäre mit try-finally vermutlich besser (oder auch mit try-with-resources)
- warum Vector und nicht irgendeine Queue? Die Threadsicherheit von Vector brauchst du nicht, da kümmerst du dich ja selbst drum
- Keine Ahnung ob ich das hier grad richtig lese/verstehe, aber wenn der Buffer einmal voll war, wird er erst wieder weiter aufgefüllt, wenn er danach wieder leer war?

- warum eine (selbstgeschriebene) Semaphore und nicht einfach irgendeinen simplen Lock aus der Concurrent-API?
- warum nicht statt den händischen get/put eine passende Queue (zB BlockingQueue dürfte ein passender Ersatz sein, wenn mich nichts täuscht)
 
Zuletzt bearbeitet:
X

Xyz1

Gast
Kann so etwas nicht vernünftig formatiert geposted werden? Dann hätte man vielleicht auch Lust, ein kostenloses Review zu machen.
 
Zuletzt bearbeitet von einem Moderator:

cspecial

Mitglied
Noch/Nur zu Buffer:

- flag1 und flag2 bleiben immer true (oder ich überseh das setzen auf false).
- die beiden sollten bessere Namen bekommen...
- buffer = new Vector<Integer> buffer; sollte vermutlich eher buffer = new Vector<>(20); sein?
- p() und v() wäre mit try-finally vermutlich besser (oder auch mit try-with-resources)
- warum Vector und nicht irgendeine Queue? Die Threadsicherheit von Vector brauchst du nicht, da kümmerst du dich ja selbst drum
- Keine Ahnung ob ich das hier grad richtig lese/verstehe, aber wenn der Buffer einmal voll war, wird er erst wieder weiter aufgefüllt, wenn er danach wieder leer war?

- warum eine (selbstgeschriebene) Semaphore und nicht einfach irgendeinen simplen Lock aus der Concurrent-API?
- warum nicht statt den händischen get/put eine passende Queue (zB BlockingQueue dürfte ein passender Ersatz sein, wenn mich nichts täuscht)

Zu 1): Nein, du hattest es richtig erkannt. Ich hatte es aber in meinem letzten Post noch einmal ausgebessert.
Zu 2): Da stimme ich zu, ich hätte die Funktion deutlicher kennzeichnen sollen.
Zu 3): Ja, das wäre die bessere Wahl, hatte es dann aber allgemein gelassen.
Zu 4): Die Datenstruktur war vorgesehen.
Zu 5): Der Puffer kann jederzeit, sobald er nicht voll ist, aufgefüllt werden.

Ich hätte noch dazu schreiben sollen, dass es mir bei der Aufgabe nur darum ging, das Produzent-Verbraucher Problem im RMI-Zusammenhang richtig gelöst zu haben.
Sollte das Programm ansonsten ordnungsgemäß laufen?

Dazu: mit Semaphore(1) kann p() zweimal aufgerufen werden, bevor blockiert wird.
Ja, es müsste dann stattdessen Semaphor(0) sein.

Vielen Dank für eure Hilfe! Ich wollte den Code noch einmal formatieren, hatte aber keine Möglichkeit mehr dazu gehabt, nachdem die ersten Antworten veröffentlicht wurden.
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Zu 1): Nein, du hattest es richtig erkannt. Ich hatte es aber in meinem letzten Post noch einmal ausgebessert.
Zu 2): Da stimme ich zu, ich hätte die Funktion deutlicher kennzeichnen sollen.
Zu 3): Ja, das wäre die bessere Wahl, hatte es dann aber allgemein gelassen.

Irgendeinen Punkt hast du übersprungen, entweder 3 oder 4...

Zu 4): Die Datenstruktur war vorgesehen.

Das bezieht sich vermutlich auf den Vector? Warum siehst du denn bei selbst gestellten Aufgaben unsinnige Datenstrukturen vor?o_O

Zu 5): Der Puffer kann jederzeit, sobald er nicht voll ist, aufgefüllt werden.
Vielleicht missversteh ich grad was, aber:

Angenommen, es sind grad 20 Einträge drin, und es wird erneut put aufgerufen.
Das if trifft zu, flag1 ist false, er landet also einfach beim wait().

Aus dem wait wacht der erst beim notify auf, und das passiert nur, wenn vorher in get die Liste einmal leer war.



Ich glaube auch, dass die flags falsch sind.
flag1 bedeutet laut dir, dass "mindestens ein Konsument im Blockiert-Zustand ist (Puffer leer)", aktuell ist der aber wahr, wenn irgendwann mal der Puffer leer war, und dann solange, wie der Puffer nie ganz voll war.
Für flag2 gilt umgekehrt das gleiche.

Sollte das Programm ansonsten ordnungsgemäß laufen?
Ich würde nicht drauf vertrauen...
Schreib dir einfach mal 'nen Test für den Buffer, um sicher zu gehen, dass der ohne RMI der läuft ;)
 

cspecial

Mitglied
Irgendeinen Punkt hast du übersprungen, entweder 3 oder 4...



Das bezieht sich vermutlich auf den Vector? Warum siehst du denn bei selbst gestellten Aufgaben unsinnige Datenstrukturen vor?o_O


Vielleicht missversteh ich grad was, aber:

Angenommen, es sind grad 20 Einträge drin, und es wird erneut put aufgerufen.
Das if trifft zu, flag1 ist false, er landet also einfach beim wait().

Aus dem wait wacht der erst beim notify auf, und das passiert nur, wenn vorher in get die Liste einmal leer war.



Ich glaube auch, dass die flags falsch sind.
flag1 bedeutet laut dir, dass "mindestens ein Konsument im Blockiert-Zustand ist (Puffer leer)", aktuell ist der aber wahr, wenn irgendwann mal der Puffer leer war, und dann solange, wie der Puffer nie ganz voll war.
Für flag2 gilt umgekehrt das gleiche.


Ich würde nicht drauf vertrauen...
Schreib dir einfach mal 'nen Test für den Buffer, um sicher zu gehen, dass der ohne RMI der läuft ;)

Warum sollte die Datenstruktur unsinnig sein? Sie arbeitet wie ein Stack und erfüllt ihren Zweck. Wie gesagt, mir geht es eher um den RMI-Aspekt.

Ansonsten stimmen die Aussagen zu den Flags, hier mal eine kleine Verbesserung:
Java:
public synchronized Integer get()
   {
      while (buffer.size() == 0)
      {
         flag1 = true;

         if(flag2)
         {
            notifyAll();
            flag2 = false;
         }

         try
         {
            wait();
         } catch(Exception e) {}

       }

       mutex.p();
       Integer temp = buffer.lastElement();
       buffer.remove(temp);
       mutex.v();
        
       if (flag2)
       {
          notifyAll();
          flag2 = false;
       }
      
       return temp;
    }


    public synchronized void put(Integer a)
   {
       mutex.p();
       buffer.add(a);
       mutex.v();
      
       if (flag1)
       {
          notifyAll();
          flag1 = false;
       }
      
       if (buffer.size() == 20)
       {
         flag2 = true;
        
         try
         {
            wait();
         } catch(Exception e)         {}
       }
    }

Es wäre auch besser, für den Produzenten und die Konsumenten jeweils eine Lock-Variable zu definieren, dann müssten nicht nach Erkennen eines gesetzten Flags alle aus dem blockiert-Zustand in rechenbereit überführt werden, sondern nur die, für die es gedacht ist.

Gruß
cspecial
 

mrBrown

Super-Moderator
Mitarbeiter
Warum sollte die Datenstruktur unsinnig sein? Sie arbeitet wie ein Stack und erfüllt ihren Zweck.
Aus dem Javadoc zu Vector:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Vector.html hat gesagt.:
If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector.

Wenn du Stack-Verhalten willst, würde ich statt Vector lieber Stack nutzen - da drückt der Typ schon aus, als was es gedacht ist (und temp ist dann überflüssig).
Wobei dann da wiederum auch gilt:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Stack.html hat gesagt.:
A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class.


Wie gesagt, mir geht es eher um den RMI-Aspekt.
Der ist vermutlich passend, aber das sind ja kaum drei Zeilen...

Ansonsten stimmen die Aussagen zu den Flags, hier mal eine kleine Verbesserung:
Die Begrenzung auf 20 gilt aber jetzt nur so lange, wie genau ein Thread als Producer fungiert. Bei 1+N Producern liegt die Obergrenze bei 20+N. Verbesserung würd ich das also nicht nennen ;)

Es wäre auch besser, für den Produzenten und die Konsumenten jeweils eine Lock-Variable zu definieren, dann müssten nicht nach Erkennen eines gesetzten Flags alle aus dem blockiert-Zustand in rechenbereit überführt werden, sondern nur die, für die es gedacht ist.
Sind die Flags überhaupt nötig? Theoretisch könnte man die weglassen, als "Flags" reichen die Tests auf min- und maximale Größe. Nach jedem rausnehmen und reinlegen weckt man einfach alle auf, die prüfen jeweils ob sie was machen können. Bin grad nur unterwegs, vielleicht hab ich da auch grad einen Denkfehler drin...die Flags dürften dabei uU nur einen kleinen Performance-Vorteil bringen, aber ob der die Komplexität wert ist?


Wofür ist eigentlich der extra Lock um put und lastElement da? Die Methode selbst ist doch schon synchronized.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
I Socket Problem mit den WebSocket Antworten der Discord API Netzwerkprogrammierung 0
K Java Websocketserver Problem | Android to Pi Netzwerkprogrammierung 1
B Socket Bilder verschicken via Sockets. Heap-Problem. Netzwerkprogrammierung 2
S Problem bei dem Bluetoothverbindungsaufbau Netzwerkprogrammierung 2
G Server-Client IO Problem Netzwerkprogrammierung 6
M Netty - TCP Problem Netzwerkprogrammierung 4
L Socket Problem mit Server Netzwerkprogrammierung 1
J Chat Server starten über GUI problem Netzwerkprogrammierung 4
M Problem bei Socket (MultiplayerSpiel) Netzwerkprogrammierung 4
M Socket CDI, Websocket reference Problem ! Netzwerkprogrammierung 2
Shams Problem mit Eventbus in Verbindung mit Server Netzwerkprogrammierung 0
G apache httpClient Problem. Netzwerkprogrammierung 5
H Problem mit ObjectStreams Netzwerkprogrammierung 3
A Problem beim Senden von Client zu Server Netzwerkprogrammierung 10
D Socket BufferedWriter/Reader Problem Netzwerkprogrammierung 1
Maxim6394 Problem mit Socks5 Implementierung Netzwerkprogrammierung 0
C Handle Connection Problem Netzwerkprogrammierung 3
E HttpUrlConnection Cookie Problem Netzwerkprogrammierung 0
X Problem mit vielen Bytes über Socket Netzwerkprogrammierung 23
O 4Gewinnt Multiplayer - Netzwerk Problem (TCP) Netzwerkprogrammierung 1
A Socket Socket-Problem - Object wird nicht übertragen Netzwerkprogrammierung 3
R Problem beim Programmieren eines Chatprogramms Netzwerkprogrammierung 5
E einfaches Problem: Session-Handling bei Servlets Netzwerkprogrammierung 5
G Problem mit einem FileWatcher Netzwerkprogrammierung 7
T Socket Server starten Thread Problem Netzwerkprogrammierung 12
B Client/Server Connection Problem Netzwerkprogrammierung 2
G Problem mit STATIC-Verständnis Netzwerkprogrammierung 8
S Umstellung AS400 auf Postgre - Problem beim Arbeiten mit Metadaten Netzwerkprogrammierung 2
J Facelets Include Rendered Problem Netzwerkprogrammierung 2
J Socket Problem mit C++/Java Netzwerkprogrammierung 20
P Problem mit Datagram-Sockets Netzwerkprogrammierung 2
G Socket NIO2 Problem mit AsynchronousSocketChannel beim Schließen Netzwerkprogrammierung 3
G Cookie Verwaltungs Problem nach Login auf InetSeite (Wo utma-Cookie?) Netzwerkprogrammierung 18
C Socket Problem mit ObjectInput/OutputSream Netzwerkprogrammierung 7
B Socket Problem mit Netzwerkchat Netzwerkprogrammierung 21
D RMI Problem beim shutdown von verteilter CORBA-Anwendung Netzwerkprogrammierung 6
Maxim6394 ipv6 Problem Netzwerkprogrammierung 2
Maxim6394 Proxyserver Performance Problem Netzwerkprogrammierung 11
M Problem Client - Server Sockets: .ready() wird nie true! Netzwerkprogrammierung 6
C Socket Problem mit ObjectInput/OutputSream Netzwerkprogrammierung 5
B RMI und Problem mit rmic-Tool Netzwerkprogrammierung 3
C FTP storeFileStream Problem Netzwerkprogrammierung 3
N Problem über http eine Datei zu senden Netzwerkprogrammierung 4
D JavaMail - Mailsabrufen Problem (imap) Netzwerkprogrammierung 12
J HTTP Übersetzung yahoo babelfish - Zeichensatz-Problem Netzwerkprogrammierung 6
D Apache CXF, JAX-WS Problem bei Arrays - einfacher Server Netzwerkprogrammierung 2
M Problem beim Datenempfang Netzwerkprogrammierung 2
X Problem mit Server-Client-Kommunikation Netzwerkprogrammierung 14
M Problem mit Socket-Verbindung Netzwerkprogrammierung 2
N NIO Problem beim speziellen Behandeln von einzelnen Benutzern Netzwerkprogrammierung 13
D Thread problem Netzwerkprogrammierung 3
T Servlets JSP: Tomcat Problem Netzwerkprogrammierung 4
K Client - Server Problem Netzwerkprogrammierung 16
T RMI Problem Client-Server Netzwerkprogrammierung 2
P RMI Stub Problem Netzwerkprogrammierung 3
D Socket UDP Übertragungs Problem Netzwerkprogrammierung 7
I HTTP Post aus html in Java einlesen - Problem Netzwerkprogrammierung 2
I HTTP Post aus html in Java einlesen - Problem Netzwerkprogrammierung 6
D Problem mit ObjectInputStreams Netzwerkprogrammierung 10
D Socket Problem mit InputStreamReader Netzwerkprogrammierung 3
N CRC32 CheckSum Problem bei UDP Netzwerkprogrammierung 2
V Java Mail Api - IMAP Problem Netzwerkprogrammierung 6
P RMI Problem Netzwerkprogrammierung 4
I Problem bei Outputstreamerzeugung in anderer Klasse als Socket Netzwerkprogrammierung 5
S Socket Problem mit Objektübertragung Netzwerkprogrammierung 16
Shoox Reader / Writer Problem Netzwerkprogrammierung 2
S Problem mit 2 Serversockets unter Win XP Netzwerkprogrammierung 7
P Socket Problem mit Netzwerkverbindung über TCP Netzwerkprogrammierung 12
M RMI - Connection Problem Netzwerkprogrammierung 7
J Socket Client - Server Problem Netzwerkprogrammierung 4
M Socket Chat-Client-Problem Netzwerkprogrammierung 8
D Client Server Problem, Methode readline() löst SocketException "Connection reset" aus Netzwerkprogrammierung 8
E HTTP Problem beim Auslesen von Websiten Netzwerkprogrammierung 6
T Problem bei Findung der richtigen Architektur Netzwerkprogrammierung 3
AlexSpritze Authentication Problem bei WebServices Netzwerkprogrammierung 4
J Socket Problem -Proxies Netzwerkprogrammierung 2
R HTTP Problem bei Authentifizierung über (Http)UrlConnection Netzwerkprogrammierung 2
F Konzept Problem Netzwerkprogrammierung 12
G NIO Sockets Architektur Problem Netzwerkprogrammierung 2
H Neues NIO problem nachricht an alle clients Netzwerkprogrammierung 3
J Client server problem Netzwerkprogrammierung 3
S Problem bzgl. Sockets / Server-Client-Struktur Netzwerkprogrammierung 3
W RMI Problem Netzwerkprogrammierung 4
M Problem mit Server Netzwerkprogrammierung 5
N Socket Java server c# client problem Netzwerkprogrammierung 7
Y Problem mit ObjectInputStream beim lesen vom Socket Netzwerkprogrammierung 10
R RMI Hibernate Problem Netzwerkprogrammierung 4
Z RMI Problem Netzwerkprogrammierung 11
F RMI problem mit RMI Netzwerkprogrammierung 3
H Object Cast Problem nach Übertragung mit Log4j Netzwerkprogrammierung 5
H Log4j SocketAppender Problem Netzwerkprogrammierung 2
P HTTP Problem beim Download von einer Datei Netzwerkprogrammierung 4
P Problem mit JOscarLib Netzwerkprogrammierung 6
0din SMTP Client - readline problem Netzwerkprogrammierung 4
C komisches Problem / ObjectInputStream erstellt sich nicht Netzwerkprogrammierung 5
W vista / win server 2003 netzlaufwerk problem Netzwerkprogrammierung 4
W Problem mit HTTP-Dateiübertragung Netzwerkprogrammierung 6
E Problem: netbeans 6.5 und webanwendung Netzwerkprogrammierung 6
C Problem mit Übertragung einer Klasse in RMI Netzwerkprogrammierung 10
B UDP Problem Netzwerkprogrammierung 25

Ähnliche Java Themen

Neue Themen


Oben