SDA SE Wiki

Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering

User Tools

Site Tools


Die folgende Situation ist hinreichend bekannt, weil häufig anzutreffen:

Beim absetzen der Events gibt es dann evtl ein Problem:

  private void fireMessageRecieved(MessageEvent m) {
    Vector cloned = null;
    synchronized (listeners){
      cloned = (Vector)listeners.clone();			
    }
    for(int i=0;i<cloned.size();i++){
      MessageListener l = (MessageListener )cloned.get(i);
      l.messageRecieved(m); //BANG! (if listener is not thread safe)
    }	
  }

  //(...)
}

Wer schon mal mit JFC/Swing zu tun hatte kennt vermutlich die Lösung:

javax.swing.SwingUtillities.invokeAndWait() // (blocking)

bzw

javax.swing.SwingUtillities.invokeLater() // (non-blocking)

Beide methoden erwarten als Argument ein Runnable, das dann auf dem AWT EPT ausgeführt wird. Entsprechend gibt es auch für SWT die folgenden beiden Methoden:

org.eclipse.swt.widgets.Display.syncExec() // (blocking)

bzw

org.eclipse.swt.widgets.Display.asyncExec() // (non-blocking)

Ähnlich wie bei den Swing Methoden oben stellen diese sicher, daß das übergebene Runnable auf dem zum jeweiligen Display object gehörendem EPT aufgerufen wird.TODO (Details zur Beziehung Display↔EPT !!! Noch unvollständig aber ein Anfang: eventprocessingthread

Gerne würden man dieses Runnable als anonyme innere Klasse implementieren:

  private void fireMessageRecieved(MessageEvent m) {
    Vector cloned = null;
    synchronized (listeners){
      cloned = (Vector)listeners.clone();			
    }
    for(int i=0;i<cloned.size();i++){
      MessageListener l = (MessageListener )cloned.get(i);
      (magicalyKnownDisplayInstance).asyncExec(new Runnable(){
         public void run(){
           l.messageRecieved(m); // Compiler will complain here!
         }
      });      
    }	
  }
}

Geht aber nicht: Innerhalb des Runnables dürfte auf m nur zugegriffen werden wenn es final deklariert wäre. (Warum eigentlich??)

Annonyme Klassen in Java haben nur Zugriff auf die final Variablen und Methodenparameter der umschließenden Methode, da diese Variablen als implizite Parameter an den Konstruktor der anonymen Klasse übergeben werden und in Feldern gespeichert werden.
Dies ist notwendig, da Variablen innerhalb eines Methodenblocks nach Ablauf der Methode wieder vom Stack genommen werden, also nach dem Methodenaufruf nicht mehr verfügbar wären.
Sollten auch alle nicht final Variablen mit in diesen Prozess einbezogen werden, wäre die Semantik einer Zuweisung nach der Objekterzeugung aber nicht klar. Soll dann auch in der Instanz der anonymen Klasse das Feld neu gesetzte werden und wie?
Und das Threadsafe?
Um diese Problematik zu vermeiden darf man nur final Variablen referenzieren, was auch ausreicht. Bezogen auf dein Beispiel kannst du einfach schreiben:

private void fireMessageRecieved(final MessageEvent m) {
...
(magicalyKnownDisplayInstance).asyncExec(new Runnable(){
public void run(){
l.messageRecieved(m); // Compiler will NOT complain here!
...
}

Deine Lösung ist natürlich möglich. Du hast hier explizt gemacht, was der Compiler automatisch generieren würde, wenn du den Methodenparameter final machst!

Das wiederum geht häufig nicht, oder führt zu häßlichen Konstruktionen.

ok, es geht schon :-) Wenn ich jetzt so drüber nachdenke, ist es tatsächlich ziemlich gleiche wie mein Ansatz unten. Meine persönliche Lösung des Problems: Definiere eine innere Klasse, die Runnable um eine Winzigkeit erweitert:

private abstract class EventRunner implements Runnable{
  java.util.EventObject ev = null;
  public EventRunner(java.util.EventObject ev){
    this.ev = ev;
  }
}

Die Methode run() implementiert man dann wie gehabt in einer anonymen Klasse:

      (...)

      (magicalyKnownDisplayInstance).asyncExec(new EventRunner(m){
         public void run(){
           l.messageRecieved( (MessageEvent) ev); 
         }
      });      

      (...)
   


AnmerkungMan beachte (magicalyKnownDisplayInstance) im obigen Beispiel! Die Frage wo (i.e. in welcher Klasse) die Entkoppelung stattfinden soll ist keineswegs trivial. Hier wäre es vermutlich sinnvoller gewesen, diese Aufgabe einem der UI näher stehendem Object anzuvertrauen, insbesondere einem das weiß auf welchem (Display-)Thread die Runnables ausgeführt werden müssen. Diskusion dieser Frage gibt's hier:threadinguisyncresponsibility

research/jtransformer/threadingnotifyui.txt · Last modified: 2018/05/09 01:59 (external edit)

SEWiki, © 2019