Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Java en Swing] InvokeLater

Pagina: 1
Acties:

  • Lethalis
  • Registratie: April 2002
  • Niet online
Hallo,

Sinds ik een cursus Objectgeorienteerd Programmeren met Java (eerst deel 1 en nu deel 2) aan de Open Universiteit volg, vind ik het ook leuk om in mijn vrijetijd een beetje te spelen met Java. Disclaimer: overdag ben ik een .NET ontwikkelaar.

Anyways, ik heb een programma gemaakt dat de standard output van een extern proces leest (rsync) in een aparte thread en de regels daarvan doorgeeft aan de event dispatch thread, zodat ik deze regels kan weergeven in mijn Swing user interface. Dit gebeurt overigens met Java 7 met NetBeans op OS X Mavericks, hoewel dat geen invloed zou mogen hebben (vind ik, maar je weet het nooit :D ).

De thread wordt als volgt gestart:

code:
1
2
3
4
5
6
7
8
9
10
11
12
  public void runBackupAsync()
  {
    Thread worker = new Thread() {
      @Override
      public void run() {
        runBackupWork();
      }
    };
    
    worker.start();
    
  }


Vervolgens is er een functie die de voortgang bijwerkt:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  private void updateProgress(final String message) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        
        itemCount++;
        itemState = itemCount % 500;
        
        if (itemState == 0)
        {
          lblidle.setText(itemCount + "");
        }
        
        System.out.println(message);
      }
    });
  }


Het symptoom dat ik heb nu: de eerste paar regels worden vloeiend bijgewerkt. Zoals jullie aan de code kunnen zien heb ik er al voor gezorgd dat een Swing UI update alleen eens per 500 updates voorkomt, om de oorzaak te achterhalen.

Wat ik nu krijg, is dat het getal netjes doortelt, dus 0, 500, 1000, 1500, 2000 enzovoorts.. totdat het ergens bij de 4500 aankomt en dan houdt mijn user interface praktisch op met werken en skipt hij zo naar de 23000.

Het hoge aantal InvokeLater requests zorgt er dus voor dat Swing over zijn nek gaat, ook al voer je verder geen intensieve taken daarin uit.

Mijn vraag is nu: hoe lossen jullie Java ontwikkelaars dit op? :D

Bij een Google zoektocht kom ik vooral mensen tegen die roepen dat je SwingWorker moet gebruiken, maar de class die ik gemaakt heb, SyncBackup is zowel te gebruiken vanuit Swing alsook de commandline en geeft updates door via een SyncBackupObserver interface die door de caller wordt geimplementeerd. InvokeLater gebruiken in het Swing gedeelte leek mij dus de eenvoudigste en meest onafhankelijke manier om te syncen met de event dispatch thread zonder mijn SyncBackup package afhankelijk te maken van Swing.

Wel is het zo dat mijn SyncBackup class nu zelf een Thread spawnt, en dit in theorie niet per se op deze plek hoeft te gebeuren (ik zou immers met een SwingWorker misschien de thread kunnen maken ipv deze verantwoordelijkheid bij mijn SyncBackup class te leggen).

Resumerend heb ik dus een SyncBackup class die status updates aan een SyncBackupObserver doorgeeft. Mijn JFrame implementeert de SyncBackupObserver interface en krijgt dan bijvoorbeeld een backupItemProcessed door, welke weer bovenstaande updateProgress functie aanroept. Uiteraard zijn dit nogal wat events. In mijn testomgeving zijn dit er 23160 :) Kortom: 23160 items die ik op mijn JFrame snel voorbij wil zien komen zonder dat het traag wordt. Ik wil namelijk ook de mogelijkheid inbouwen om te kunnen annuleren tussentijds.

De updateProgress functie wordt iig goed aangeroepen, want de System.out.println(message); werkt goed en ik zie in het output window van NetBeans alle items voorbij komen die ik graag op mijn JFrame wil tonen.

Hoe zouden jullie dit oplossen?

Is het echt zo dat SwingWorker veel sneller is dan InvokeLater en moet ik de boel maar gaan ombouwen? :) Of is dit onzin? Want ik lees ook berichten van mensen die zeggen dat het qua performance niet uitmaakt :/

PS:
Dit is een hobby project. Ik weet dat er al 300+ rsync gui's zijn :D Maar ik doe het vooral om mijn begrip van Java te verbeteren en probeer alle onderwerpen van de cursus er in te verwerken.

Ask yourself if you are happy and then you cease to be.


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Dit is volgens mij niet anders in .NET. Het is in beide frameworks niet de bedoeling om teveel events over de dispatcher te gooien. Dus SwingUtilities.invokeLater moet je dan maar 1 keer per zeg 500 items uitvoeren, en niet de berekening in de run van invokeLater doen.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • Lethalis
  • Registratie: April 2002
  • Niet online
Ik ga dit testen.. ook met .net want ik ben nu nog in de veronderstelling dat het daar wel blijft werken. Zal wel een apart testproject in elkaar draaien hiervoor.

[edit]
Volgens mij komt het niet zozeer door het aantal events, maar gewoon door het feit dat de child thread geen sleep momenten had en daardoor andere threads minder vaak aan de bak komen. In mijn testproject waar ik een teller laat lopen, moest ik ook een Thread.sleep inbouwen van bijvoorbeeld 10 ms zodat de boel gaat lopen zonder issues :)

Kortom, ik heb met thread starvation te maken nu. En ik heb het opgelost door nu per X aantal items een sleep van 10ms in te bouwen.

[edit2]
Ik heb dat laatste een beetje getuned nu. Eens per 50 items doe ik een sleep van 5 ms. Ik heb ook uitgerekend wat de performance impact hiervan zou zijn. Bij 23000 items gaat het om een theoretische vertraging van 2,3 seconden. Dat vind ik acceptabel. Ik zal nog echt moeten meten of dit ook klopt, maar het programma blijft nu iig vloeiend werken :)

Ook zou het volgens de documentatie zo moeten zijn dat ik hierdoor geen meldingen misloop:

http://docs.oracle.com/ja.../java/awt/EventQueue.html

[ Voor 90% gewijzigd door Lethalis op 15-02-2014 08:23 ]

Ask yourself if you are happy and then you cease to be.