# RMI Threads und Synchronized



## Tallan (24. Nov 2009)

Hallo Zusammen,

ich habe ein paar Verständnisfragen zu RMI in Verbindung mit Threads.

Soweit ich gelesen haben wird wenn Client A eine Methode auf dem Server aufruft ein Thread erstellt.
Ruft Client B nun die selbe Methode auf wird ein weiterer Thread erstellt. (Client A und B haben jeweils eine eigene VM)

Wenn A oder B jetzt weiterhin diese Methode aufrufen und der jeweilige alte Thread noch nicht beendet ist ( ist ja z.B durch Clientseitige Threads möglich), ist nicht sichergestellt ob ein 2ter Thread pro Client auf dem Server erzeugt wird oder die Methode in dem vorher erstellten weiterläuft.
So zumindest der Englische Text :


```
Server-side threads in Java RMI

One method of improving a server’s performance is to create a thread for each remote method 
invocation, allowing multiple calls to be processed concurrently. Therefore, several clients are not
 blocked in a queue waiting for their call to be executed. Java RMI automatically provides you with 
this level of server-side threading; this policy is described as follows in the RMI specification:



“A method dispatched by the RMI runtime to a remote object implementation (a server) may or may 
not execute in a separate thread. Calls originating from different clients Virtual Machines will execute 
in different threads. From the same client machine it is not guaranteed that each method will run in a 
separate thread”



Therefore, if you make remote calls from separate clients (executing in different JVMs) each call will
 run in a separate thread. However, if you make concurrent calls from the same client (this can be 
acheived by using client-side threading, see later) then it is possible these calls will execute on the 
same server thread.
```

Synchronized sorgt dafür das eine Methode nicht zeitgleich ausgeführt wird, stellt also eine Art FiFo liste dar welche die Aufrufe nach und nach abarbeitet.

Da hier aber ein Thread erstellt wird wäre der Methodenaufruf doch in dem Moment in dem der Thread startet beendet und der folgende Aufruf würde gestartet ohne das der 1ste u.U. fertig ist, oder wird das intern abgefragt?


Weiterhin würde mich interessieren ob es eine Möglichkeit gibt diese "automatische" Erstellung von Threads zu unterbinden, da ich die Anzahl der Threads bei manchen Methoden gerne über einen Threadpool begrenzen würde.


----------



## tuxedo (25. Nov 2009)

Das "synchronisieren" mit "synchronized" geschieht BEVOR der Thread erstellt wird...

Damit der entfernte Methodenaufruf überhaupt funktioniert steckt viel Reflection und Proxy Technik dahinter. D.h. zwischen dem "empfangen des Methodenaufrufs via Netzwerkkarte" und "aufrufen der Methode" passt noch ne ganze Menge RMI Logik. Und genau da werden solche Dinge behandelt.

Wo hast du denn den Text her? Quelle?

*mal genauer durchlesen muss bevor ich wieder was qualifiziertes schreibe  *


----------



## Tallan (25. Nov 2009)

hier die Adresse Threading in RMI

lässt sich die threaderstellung vermeiden? Threads sollen in java ja relativ "teuer" sein,daher und um die Anzahl der Threads unter kontrolle zu halten wollte ich bei einigen methoden den threadpool nutzen

allerdings wäre eine rmi methode in der Form


```
public void remote(..) throws ...
{
     threadPool.runTask(new ....)
}
```

nicht sonderlich effektiv da beim aufruf von remote() sowieso pro client ein thread erzeugt wird, welcher zwar dann relativ schnell terminiert aber..... das gelbe vom ei scheint es mir nicht zu sein


----------



## tuxedo (25. Nov 2009)

Okay, *gelesen*
Also zu 100,00% weiß ich's auch nicht (und steht auch nicht in besagtem Link). Aber bei Java IO (und das verwendet RMI) brauchst du, um auf Serverseite mehr als einen Client bedienen zu können, pro Client einen Thread der vom Socket liest und Daten schreibt. Das ist so (völlig unabhängig von RMI). Und da kannst du auch nix wegoptimieren. 

[vermutung-modus]
Vielleicht kann RMI entscheiden ob ein Remote-Call in diesem "Netzwerkthread" stattfindet, oder ob hierfür ein extra Thread erzeugt wird.
[/vermutung-modus]

Wenn in der Tat der Netzwerkthread zum ausführen der Methode benutzt wird dann wäre das in meinen Augen jedoch "fatal". Denn wenn ein Methodenaufruf mal länger dauert, dann wäre der Netzwerkthread blockiert und du könntest von ein und demselben Client, aus einem anderen Client Thread heraus keine weitere Methoden aufrufen. Das ganze würde blockieren.

Was ich jedoch weiß: Zwischen RMI Client und Server gibts keine statische Verbindung. Die Verbindung wird "bei bedarf" (wie auch immer sich das definiert) aufgebaut und abgebaut. Du hast also sogesehen pro Client nur dann einen fixen Netzwerkthread wenn die Netzwerkverbindung gerade offen ist.

[vermutung-modus]
Ich tippe mal darauf dass in obigem Text nicht der Netzwerkthread gemeint ist, sondern ein darauf aufbauender "Workerthread". Die Daten werden vom Netzwerkthread gelesen und an den Workerthread weiter delegiert. Dieser kümmert sich um die Ausführung. Ich vermute weiterhin dass dieser Workerthread sich nach einem Task nicht gleich beendet, sondern auf den nächsten Task wartet (Also eine Art Thread-Pool mit nur einem Thread). RMI scheint weiterhin in der Lage zu sein weitere Workerthreads hinzu zu ziehen. Wie und wann: Kein Plan. 
[/vermutung-modus]

Beeinflussen kannst du das Thread-Verhalten bei RMI AFAIK nicht. Musst du auch gar nicht. Denn du versuchst hier an einer Stelle zu optimieren die (noch) gar nicht optimiert werden muss. 

Ich hab mit RMI Tests gemacht um es mit SIMON zu vergleichen. Das Ergebnis war schon krass.

RMI schafft gleichzeitige Methodenaufrufe von mehreren Clients i.d.R. binnen <5ms (localhostverbindung um das "langsame" Netzwerk mal aus dem Test weg zu lassen). Das schnellste das ich gemessen habe lag irgendwo zwischen 1,8..2,5ms!

Threads auf Serverseite sind hier also nicht dein Problem. Problematisch wird's wenn du seeehr viele Clients hast. Sagen wir mal >250 ... Und die dann auch noch ständig im <1s Rhythmus Methoden aufrufen. Dann wird's vielleicht für RMI stressig. 

Wenn es doch mehr wie 250 oder 500 Clients sein sollten schaust du dir am besten nochmal SIMON an. Tests zeigen dass rund 10.000 Methodenaufrufe pro Sekunde möglich sind.


----------



## Tallan (25. Nov 2009)

tuxedo hat gesagt.:


> Denn du versuchst hier an einer Stelle zu optimieren die (noch) gar nicht optimiert werden muss.




Das ist eben die Frage, der Client sendet einen SQL Befehl an den Server und unmittelbar danach einen weiteren ( selbe RMI Methode auf dem Server ), die GUI möchte ich nicht einfrieren allerdings sicherstellen das Anweisung 2 nicht vor Anweisung 1 durchgeführt wird.

Wenn jetzt hier 2 Threads entstehen sollten kann ich nicht sicherstellen ob Anweisung 1 wirklich als erste ausgeführt wird, es sei denn die Methode ist synchronized, allerdings hätte das wiedderum auswirken auf einen anderen Client.


----------



## tuxedo (25. Nov 2009)

Na also so langsam aber sicher stellt du dich an wie'n Anfänger...

Wenn due GUI nicht einfreieren soll: Mach nen Thread im Client oder benutze SwingWorker etc... (hatte ich ja schon erwähnt).

Wenn SQL Statement 1 nicht vor SQL Statement 2 ausgeführt werden soll: Dann hat das nix mit RMI, Netzwerk oder Threads zu tun. Benutze Transactions.

Alternativ kannst du die beiden Methodenaufrufe die jeweils eins der SQL Statements ausführen auch aus dem selben (nicht GUI-) Thread laufen lassen. Dann solltest du auf der sicheren Seite sein.

Wenn du aus einem Thread zwei Methodenaufrufe machst, dann wird von RMI und Co. sicher gestellt dass A vor B kommt und nicht B einfach A überholt. Da würde man sich ja zu tode synchronisieren...

- Alex


----------



## Tallan (25. Nov 2009)

tuxedo hat gesagt.:


> Wenn due GUI nicht einfreieren soll: Mach nen Thread im Client oder benutze SwingWorker etc... (hatte ich ja schon erwähnt).
> 
> Wenn SQL Statement 1 nicht vor SQL Statement 2 ausgeführt werden soll: Dann hat das nix mit RMI, Netzwerk oder Threads zu tun. Benutze Transactions.
> 
> - Alex



In der GUI sind natürlich Threads, das die GUI nicht einfrieren soll sagte ich nur weil das die alternative lösung wäre um sicherzustellen das 1ne Anweisung nach der anderen ausgeführt wird, da die 2te ja dann erst kommen kann wenn 1 fertig ist.....

Transaktionen in Java sind afaik nicht abgrenzbar. Und für diesen Fall auch nicht wirklich brauchbar.

Ein Beispiel damit du weißt was ich meine :

In einer Tabelle wird ein Feld geändert diese änderun soll in die Datenbank geschrieben werden
Hierfür wird per RMI eine Methode auf dem Server aufgerufen ( welcher die Anbindung an die DB hat )
und es werden die benötigten Parameter übergeben.

Weiterhin kann jetzt noch x mal etwas geändert werden oder auch nicht das hängt vom User ab.

Da mehrere User auf den Datensatz zugreifen können werden hier commitid's genutzt, d.h bei jeder Änderung in der DB wird die commitid incrementiert.

Jetzt zum Problem:

Client A macht 2 Änderungen in der Tabelle 
-> auf der Serverseite wird 2 mal eine Methode aufgerufen um die gewünschte Änderung in die DB zu schreiben. Als übergabe erhält er eine commitid von der der client ausgeht und alles was für die erstellung einer Querry benötigt wird ( value, Spalte und id der Spalte ).
Der Client sendet die commitid die er beim einlesen der Daten aus der DB erhalten hat damit sichergestellt ist das er nicht Daten in der DB überschreibt die er noch garnicht gesehen hat.

Gehen wir von commitid 1 aus die bei der ersten Änderung gesendet wird.
Jetzt entsteht ein Thread im Server der eine SQL Anweisung der Form 
"Update ...., commitid = 2 where commitid = 1"

Bei der 2ten Anweisung sendet der Client commitid 2
Hier ist jetzt der Knackpunkt, unter umständen wird ein 2ter Thread erstellt, der theoretisch vor dem 1sten Fertig sein könnte und in diesem Fall wäre die commitid 2 falsch da der erste Aufruf noch garnicht in die DB geschrieben hat.


-------------------

Transaktionen laufen wenn ich das richtig verstanden hab in Java folgendermaßen ab.

dbconection.autocommit(false);

SQL Anweisung...
SQL Anweisung...
dbconnection.execute();


Hier kann es natürlich auch Krachen

Methodenaufruf 1 ist grade mit der 1sten SQL Anweisung fertig, dann wird der 2ten Methodenaufruf in einem extra Thread komplett ausgeführt und da es sich ja um die selbe dbconnection handelt wird ein Teil des ersten Methodenaufrufs ungewollt mit commited.



tuxedo hat gesagt.:


> Alternativ kannst du die beiden Methodenaufrufe die jeweils eins der SQL Statements ausführen auch aus dem selben (nicht GUI-) Thread laufen lassen. Dann solltest du auf der sicheren Seite sein.



Du meinst also einen Threadpool mit nur einem aktiven Thread auf Clientseite der die RMI Aufrufe erledigt?




tuxedo hat gesagt.:


> Wenn du aus einem Thread zwei Methodenaufrufe machst, dann wird von RMI und Co. sicher gestellt dass A vor B kommt und nicht B einfach A überholt. Da würde man sich ja zu tode synchronisieren...



Das würde das Problem natürlich lösen, genau das war mein gedanklicher Knackpunkt.


----------



## tuxedo (25. Nov 2009)

Tallan hat gesagt.:


> Transaktionen in Java sind afaik nicht abgrenzbar. Und für diesen Fall auch nicht wirklich brauchbar.



Wer redet denn von Java? Ich meine eine DB basierte Transaction... Hibernate macht das sehr elegant.



> Da mehrere User auf den Datensatz zugreifen können werden hier commitid's genutzt, d.h bei jeder Änderung in der DB wird die commitid incrementiert.



Das hört sich für mich an als ob du ein Pseudo-Transaction neu erfunden hast ...




> Transaktionen laufen wenn ich das richtig verstanden hab in Java folgendermaßen ab.
> 
> dbconection.autocommit(false);
> 
> ...



Ich schlage vor du liest hier mal nach: Using Transactions (The Java™ Tutorials > JDBC(TM) Database Access > JDBC Basics)

Des weiteren:

Wer sagt denn dass du multithreaded auf die DB zugreifen sollst? Hab ich nicht schon zum xt-ten mal Hibernate erwähnt? Du musst die synchronisation der DB nicht im Kommunikationslayer erledigen. Wenn es auf der DB etwas zu synchronisieren gibt, dann macht man das da wo's gebraucht wird: In der Schicht in der letztendlich persistiert wird.




> Du meinst also einen Threadpool mit nur einem aktiven Thread auf Clientseite der die RMI Aufrufe erledigt?



Das wäre EINE Möglichkeit. Allerdings ist die nicht sonderlich toll. 



> Das würde das Problem natürlich lösen, genau das war mein gedanklicher Knackpunkt.



Falsch. Das würde das Problem für einen Client lösen. Denn hier wird nur sichergestellt dass ein Client die Aufruf-Reihenfolge nicht durcheinander bringt. 

Was du brauchst ist eine transaktionssichere Persistenzschicht. Hab ich Hibernate eigentlich schon erwähnt? Ach, Hibernate magst du nicht? Wie wär's mit Eclipse-Link?

Du kannst aber auch alles zu Fuß machen. Aber einfacher wird's dadurch sicher nicht. Und damit's bei all dem Text nicht in vergessenheit gerät: Das hat nix mit RMI oder dem Client zu tun 

- Alex


----------



## Tallan (25. Nov 2009)

Hibernate werde ich mir jetzt anschauen.



Zu den Transaktionen die im Tutorail beschreiben sind 


```
con.setAutoCommit(false);
PreparedStatement updateSales = con.prepareStatement(
    "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
updateSales.setInt(1, 50);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
PreparedStatement updateTotal = con.prepareStatement(
    "UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?");
updateTotal.setInt(1, 50);
updateTotal.setString(2, "Colombian");
updateTotal.executeUpdate();
con.commit();
con.setAutoCommit(true);
[code=Java]
ist genau das was ich sagte
autocommit aus
statements bauen
commiten

wenn jetzt jemand anderes ein commit auf "con" rausschickt während du beim statement bauen bist gibt es ein problem.
```


----------



## tuxedo (25. Nov 2009)

Tallan hat gesagt.:


> Hibernate werde ich mir jetzt anschauen.



EclipseLink anschauen schadet auch nicht: 
Java Persistence API (JPA) with EclipseLink - Tutorial



> wenn jetzt jemand anderes ein commit auf "con" rausschickt während du beim statement bauen bist gibt es ein problem.



Das Tutorial hört doch da nicht auf. Das geht mit Rollbacks etc noch weiter.

Das verwendete DBMS selbst beherrscht wohlmöglich auch selbst diverse Locking-Mechanismen. Da schadet ein Blick mit sicherheit auch nicht.

Ach ja: Du verwendest nicht zufällig MySQL?

btw: Die Problematik hat sich ja nun von Netzwerk zu Datenbanken verschoben. Vielleicht postest du das Problem mal im entsprechenden Board? Da springen dann auch mehr DB-Wissende rum ...


----------



## Tallan (25. Nov 2009)

doch mysql warum?


----------



## tuxedo (25. Nov 2009)

Na dann kannst du auch ruhigen gewissens SIMON verwenden. Denn du wirst ja sicher den Connector/J JDBC Treiber von MySQL in Benutzung haben um mit der DB zu sprechen ?! Der ist nämlich ebenfalls GPL lizensiert :lol:


----------



## Tallan (25. Nov 2009)

ist mir durchaus bewusst

siehe pn


----------



## tuxedo (25. Nov 2009)

Naja, die Profs und ihre Vorstellungen von Software  Ich kenn das


----------

