[java] Thread probleem i.c.m. Swing components

Pagina: 1
Acties:

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
Ik wil naast m'n swing gui een thread laten draaien die mijn GUI moet benaderen zonder dat de hele boel bevriest.
Nu heb ik in andere topics gelezen dat de combinatie van swing en threads toch lastig is. Ik zal mijn thread aan de event-dispatcher moeten toevoegen met behulp van SwingUtilities.invokeLater(Runnable job), maar ik kom er niet helemaal uit.

Ik heb het volgende gebouwd:

In een JPanel heb ik instanties aangemaakt van een JobGenerator (subclass van Thread) en een JList. De JobGenerator genereert at random een nieuwe Job, die in de JList moet worden laten zien. De model van de JList bevat dus een lijst van Job objecten die de Runnable interface implementeren.

Vanaf een abstractAction kan ik de JobGenerator starten, en het is de bedoeling dat de Job's in de listmodel worden geplaatst, en dus ook in de JList worden laten zien.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class JobGenerator extends Thread {

    private List<Job> _jobList;
    private GeneratorListModel _generatorListModel;

    public JobGenerator(String filename, GeneratorListModel generatorListModel)
                                        throws IOException, InvalidJobInputException {
        if (filename == null || filename.length() == 0)
            throw new RuntimeException("Error: filename can't be null or empty");

        if (generatorListModel == null)
            throw new NullPointerException("_generatorListModel can't be null");

        _generatorListModel = generatorListModel;

        init();
        fillJobList(filename);
    }

    private void init() {
        _jobList = new LinkedList<Job>();
    }

    private void fillJobList(String filename) throws IOException, InvalidJobInputException {
    // Lijst wordt gevuld, code niet interessant
    }

    public void run() {
        for (Iterator<Job> itt = _jobList.iterator();itt.hasNext();) {
            Job job =itt.next();
            try {
                Thread.sleep(job.getTime());
            } catch(InterruptedException e) {
                e.printStackTrace();
            }

            _generatorListModel.enqueue(job);
        }
    }
}


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class GeneratorListModel implements ListModel {

    private LinkedList<Job> _jobGeneratorQueue;
    private List<ListDataListener> _listenerList = new LinkedList<ListDataListener>();

    public GeneratorListModel() {
        _jobGeneratorQueue = new LinkedList<Job>();
    }

    public int getSize() {
        return _jobGeneratorQueue.size();
    }

    public Object getElementAt(int index) {
        return _jobGeneratorQueue.get(index);
    }

    public void addListDataListener(ListDataListener l) {
        if (l == null)
            throw new NullPointerException("l can't be null");

        _listenerList.add(l);
    }

    public void removeListDataListener(ListDataListener l) {
        if (l == null)
            throw new NullPointerException("l can't be null");

        _listenerList.remove(l);
    }

    public void enqueue(Job job) {
        if (job == null)
            throw new NullPointerException("job can't be null");

        _jobGeneratorQueue.addLast(job);

        ListDataEvent e = null;
        for (Iterator<ListDataListener> itt = _listenerList.iterator();itt.hasNext();) {
            if (e == null)
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, getSize()-1, getSize()-1);

            itt.next().intervalAdded(e);
        }
    }

    public Job dequeue() {
        Job job = _jobGeneratorQueue.removeFirst();

        ListDataEvent e = null;
        for (Iterator<ListDataListener> itt = _listenerList.iterator();itt.hasNext();) {
            if (e == null)
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, 0);

            itt.next().intervalRemoved(e);
        }

        return job;
    }
}


Wat er dus gebeurt is dat wanneer ik de abstractaction aanroep, de GUI een tijdje bevriest, en als de JobGenerator thread klaar is, dat dan pas de GUI wordt gerefresht.

Ik weet dat ik deze thread in de event dispatcher moet plaatsen, maar ik ben hier verder compleet vast gelopen. Is er iemand die mij op weg kan helpen?

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Boktor schreef op 29 augustus 2004 @ 18:01:
Ik wil naast m'n swing gui een thread laten draaien die mijn GUI moet benaderen zonder dat de hele boel bevriest.
Als je al een andere thread hebt draaien, dan kan je gui niet bevriezen ;) Dat is juist het voordeel van een 2e thread.

Als jij van die andere thread weer op de event dispatching thread wilt komen, moet je dat doen mbv een invokeLater/invokeAndWait

code:
1
2
3
4
5
class UpdateRunnable implements RUnnable{
      public void run(){
           _component.nietThreadSafeSetText("Klaar!");
      }
}


En kan je deze op de event dispatching thread op de volgende manier laten uitvoeren:

SwingUtilities.invokeLater(new UpdateRunnable());

Je weet nu zeker dat _component zijn methode (die niet thread safe is) toch vanuit de event dispatching thread wordt aangeroepen.

Ik heb niet je hele code doorgeploegd, maar ik zie geen JobGenerator.start staan. Dus zover ik kan zien wordt er geen nieuwe thread aangemaakt en dat is ook meteen een verklaring waarom je gui bevriest.

[edit]
Waarom gebruik je een ListModel? Gebruik je dit om een JList van info te voorzien?

[ Voor 7% gewijzigd door Alarmnummer op 29-08-2004 18:11 ]


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alle operaties die je op de GUI wilt doen vanuit je eigen thread moeten op de event dispatch thread geplaatst wordt. Als ik het goed zie, verzorgt deze methode de interactie met de GUI:

code:
1
_generatorListModel.enqueue(job)


Deze methode aanroep moet dus op de event dispatch thread uitgevoerd worden. Dit kan je doen door een Runnable te maken die alleen maar deze methode aanroep doet. Via SwingUtilities.invokeLater zet je deze Runnable op de event dispatch thread.

Misschien is het ook een goed idee om niet Thread te extenden maar de Runnable interface te implementeren. Je kan met die Runnable dan een Thread aanmaken. Kijk ook uit dat je nooit de run methode van de thread aanroept, want dit betekent dat je code gewoon op de huidige Thread wordt uitgevoerd, waardoor je waarschijnlijk alsnog de event dispatch thread bezig houdt. Eigenlijk zou een compiler een dikke vet error moet rapporteren als deze methode wordt aangeroepen (FindBugs doet dit ook). In plaats daarvan moet je dus start aanroepen. Ik weet niet zeker of jouw code deze fout bevat.

btw, ik zie dat je Java 1.5 constructies gebruikt (geparameterizeerde typen). Je kan dan ook de nieuwe for loop gebruiken:
Java:
1
2
3
4
5
6
for (ListDataListener listener : _listenerList) {
  if (e == null)
    e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, 0);

  listener.intervalRemoved(e);
} 

[ Voor 24% gewijzigd door mbravenboer op 29-08-2004 18:22 ]

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
Alarmnummer schreef op 29 augustus 2004 @ 18:10:
[...]

Als je al een andere thread hebt draaien, dan kan je gui niet bevriezen ;) Dat is juist het voordeel van een 2e thread.
Ja, was een beetje raar uitgelegd van mij :) Dat voordeel had ik ook gezien, maar werd niet zo uitgevoerd ;)
Als jij van die andere thread weer op de event dispatching thread wilt komen, moet je dat doen mbv een invokeLater/invokeAndWait

code:
1
2
3
4
5
class UpdateRunnable implements RUnnable{
      public void run(){
           _component.nietThreadSafeSetText("Klaar!");
      }
}


En kan je deze op de event dispatching thread op de volgende manier laten uitvoeren:

SwingUtilities.invokeLater(new UpdateRunnable());

Je weet nu zeker dat _component zijn methode (die niet thread safe is) toch vanuit de event dispatching thread wordt aangeroepen.
Oke, ik begrijp het probleem nu, GUI updates moeten dus vanuit de event dispatcher worden uitgevoerd. Alleen de uitvoering vind ik nog wat lastig, je weet dat ik een hekel heb aan Threads ;) Maar als ik het goed begrijp maak ik een instantie van de UpdateRunnable in mijn JobGenerator thread. Telkens als er iets verandert in mijn generator,moet ik een nieuwe UpdateRunnable maken?
Ik heb niet je hele code doorgeploegd, maar ik zie geen JobGenerator.start staan. Dus zover ik kan zien wordt er geen nieuwe thread aangemaakt en dat is ook meteen een verklaring waarom je gui bevriest.

[edit]
Waarom gebruik je een ListModel? Gebruik je dit om een JList van info te voorzien?
Ik gebruik idd een ListModel om mijn JList van info te voorzien. In die ListModel komen Jobs te staan, ook een implementatie van Runnable, dus hier kom ik waarschijnlijk hetzelfde probleem tegen.

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
mbravenboer schreef op 29 augustus 2004 @ 18:15:
Alle operaties die je op de GUI wilt doen vanuit je eigen thread moeten op de event dispatch thread geplaatst wordt. Als ik het goed zie, verzorgt deze methode de interactie met de GUI:

code:
1
_generatorListModel.enqueue(job)


Deze methode aanroep moet dus op de event dispatch thread uitgevoerd worden. Dit kan je doen door een Runnable te maken die alleen maar deze methode aanroep doet. Via SwingUtilities.invokeLater zet je deze Runnable op de event dispatch thread.
Hetzelfde als hoe Alarmnummer het uit had gelegd. Al heb ik nog niet helemaal door hoe dit moet (ik heb threads altijd al een erg lastig onderwerp gevonden in java) zal ik hier verder mee spitten.
Misschien is het ook een goed idee om niet Thread te extenden maar de Runnable interface te implementeren. Je kan met die Runnable dan een Thread aanmaken. Kijk ook uit dat je nooit de run methode van de thread aanroept, want dit betekent dat je code gewoon op de huidige Thread wordt uitgevoerd, waardoor je waarschijnlijk alsnog de event dispatch thread bezig houdt. Eigenlijk zou een compiler een dikke vet error moet rapporteren als deze methode wordt aangeroepen (FindBugs doet dit ook). In plaats daarvan moet je dus start aanroepen. Ik weet niet zeker of jouw code deze fout bevat.
Grappig is dat ik zojuist nog heb gezocht wat precies het verschil is wanneer ik een subclass maak van Thread, of de Runnable interface implementeer. Helaas geen duidelijk verhaal gevonden, maar het is me nu duidelijk, thanx :)
btw, ik zie dat je Java 1.5 constructies gebruikt (geparameterizeerde typen). Je kan dan ook de nieuwe for loop gebruiken:
Java:
1
2
3
4
5
6
for (ListDataListener listener : _listenerList) {
  if (e == null)
    e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, 0);

  listener.intervalRemoved(e);
} 
hee die is handig, thanx :)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Boktor: Hetzelfde als hoe Alarmnummer het uit had gelegd.
Uiteraard, stel je voor dat we elkaar tegen zouden spreken! :+
Al heb ik nog niet helemaal door hoe dit moet (ik heb threads altijd al een erg lastig onderwerp gevonden in java) zal ik hier verder mee spitten.
Het is niet zo lastig als het allemaal klinkt, zolang je maar binnen de standaard patronen blijft. Het plaatsen van een Runnable op de event dispatch thread kan je zo doen (niet getest) :

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void run()
{
  for (final Job job : _jobList)
  {
    try
    {
      Thread.sleep(job.getTime());
    }
    catch(InterruptedException e)
    {
      e.printStackTrace();
    }

    SwingUtilities.invokeLater(
      new Runnable()
      {
        public void run()
        {
          _generatorListModel.enqueue(job);
        }
      });
  }
}

Let op dat lokale variabelen final moeten zijn als je ze wilt gebruiken in zo'n anonieme lokale klasse.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
mbravenboer schreef op 29 augustus 2004 @ 18:49:
[...]

Uiteraard, stel je voor dat we elkaar tegen zouden spreken! :+
:D
[...]

Het is niet zo lastig als het allemaal klinkt, zolang je maar binnen de standaard patronen blijft. Het plaatsen van een Runnable op de event dispatch thread kan je zo doen (niet getest) :

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void run()
{
  for (final Job job : _jobList)
  {
    try
    {
      Thread.sleep(job.getTime());
    }
    catch(InterruptedException e)
    {
      e.printStackTrace();
    }

    SwingUtilities.invokeLater(
      new Runnable()
      {
        public void run()
        {
          _generatorListModel.enqueue(job);
        }
      });
  }
}

Let op dat lokale variabelen final moeten zijn als je ze wilt gebruiken in zo'n anonieme lokale klasse.
Bedankt! Het werkt nu perfect!

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
Nog even een ander vraagje. Ik wil nu de threads in een LinkedList verzamelen, zodat ik hier een ListModel omheen kan bouwen en de Threads visueel kan maken in een JList component.


dit is de code van de implementatie van ListModel:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class GeneratorListModel implements ListModel {

    private JobList _jobList;
    private List<ListDataListener> _listenerList = new LinkedList<ListDataListener>();

    public GeneratorListModel() {
        _jobList = new JobList(Setup.JOBLISTMAXSIZE);
    }

    public int getSize() {
        return _jobList.size();
    }

    public Object getElementAt(int index) {
        return _jobList.get(index);
    }

    public void addListDataListener(ListDataListener l) {
        if (l == null)
            throw new NullPointerException("l can't be null");

        _listenerList.add(l);
    }

    public void removeListDataListener(ListDataListener l) {
        if (l == null)
            throw new NullPointerException("l can't be null");

        _listenerList.remove(l);
    }

    public void enqueue(JobThread jobThread) {
        if (jobThread == null)
            throw new NullPointerException("jobs can't be null");

        _jobList.addJob(jobThread);

        ListDataEvent e = null;
        for (Iterator<ListDataListener> itt = _listenerList.iterator();itt.hasNext();) {
            if (e == null)
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, getSize()-1, getSize()-1);

            itt.next().intervalAdded(e);
        }
    }

    public Thread dequeue() {

        Thread jobThread = _jobList.getJob();
        ListDataEvent e = null;
        for (Iterator<ListDataListener> itt = _listenerList.iterator();itt.hasNext();) {
            if (e == null)
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, 0);

            itt.next().intervalRemoved(e);
        }

        return jobThread;
    }



en de code van JobList:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class JobList {

    private int _maxSize;
    private List<JobThread> _jobList = Collections.synchronizedList(new LinkedList<JobThread>());

    public JobList(int maxSize) {
        _maxSize = maxSize;
        _jobList.add(new JobThread(new Job(0,0,-1,0,0)));
    }

    public int getMaxSize() {
        return _maxSize;
    }

    synchronized public JobThread getJob() {

        return _jobList.remove(0);
    }

    synchronized public boolean addJob(final JobThread jobThread) {

        if (_maxSize <= _jobList.size())
            return false;

        putBack(jobThread);
        return true;
    }

    synchronized public void putBack(JobThread jobThread) {
        if (_maxSize <= _jobList.size())
            throw new RuntimeException("maximum size smaller or equal to joblist size");

        double currentTime = System.currentTimeMillis();
        double timeSuspended = currentTime - jobThread.getJob().getLastInvoked();
        double priority = (double)jobThread.getJob().getPriority();
        double amount = Setup.getAmount(timeSuspended,priority);

        for (int i=0;i<_jobList.size();i++) {
            JobThread job = _jobList.get(i);
            if (amount >= Setup.getAmount(currentTime-job.getJob().getLastInvoked(),job.getJob().getPriority())) {
                _jobList.add(i, jobThread);
            }
        }
    }

    public int size() {
        return _jobList.size();
    }

    public JobThread get(int index) {
        return _jobList.get(index);
    }
}



In het begin van dit draadje heb ik de code geplaatst van de jobgenerator, hiervan heb ik de run-methode als volgt aangepast:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void run() {
        for (final Job job : _jobList) {

            try {
                Thread.sleep(job.getTime());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    _generatorListModel.enqueue(new JobThread(job));
                }
            });
        }
    }



Ik enqueue de listModel vanuit de eventdispatcher, maar zogauw ik de jobgenerator thread opstart (met start() uiteraard), blijft m'n gui hangen.


Wie kan mij verder op weg helpen? Ik krijg het absoluut niet voor elkaar ;(

Verwijderd

[lichtelijk offtopic]
mbravenboer schreef op 29 augustus 2004 @ 18:49:

Java:
1
2
3
4
5
6
7
8
9
10
...

    SwingUtilities.invokeLater(
      new Runnable()
      {
        public void run()
        {
          _generatorListModel.enqueue(job);
        }
      }); 
Het is voor dit soort standaard patronen eigenlijk jammer dat er niet zoiets bestaat als 'statement' references. Je had anders iets op kunnen schrijven als:

Java:
1
    SwingUtilities.invokeLater( _generatorListModel.enqueue(job);  ); 


Je zou dit mischien alleen dmv compiler support kunnen implementeren, door deze constructie automatisch te vervangen door de eerste.

In de tussentijd kun je ook je code een beetje anders formateren, zodat je iets meer de nadruk legt op de statements die later ge-invoked worden.

Java:
1
2
3
    SwingUtilities.invokeLater( new Runnable() {public void run() {          
           _generatorListModel.enqueue(job);     
    }}); 


Maar deze laatste vorm zal vast niet iedereen hier even mooi vinden ;)

[/lichtelijk offtopic]

Verwijderd

Imo kan je best even dit artikel nalezen. Die invokeLater stuff wordt wat te vaak verkeerd gebruikt.

http://today.java.net/pub/a/today/2003/10/24/swing.html

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
lichtelijk offtopic
Off-topic wordt het meestal pas echt leuk ;)
henk_DE_man: Het is voor dit soort standaard patronen eigenlijk jammer dat er niet zoiets bestaat als 'statement' references. Je had anders iets op kunnen schrijven als:

Java:
1
SwingUtilities.invokeLater( _generatorListModel.enqueue(job);  ); 
Inderdaad, dit worden ook wel closures genoemd. Vrijwel alle functionele (Lisp, Haskell, etc.) supporten closures. Python support closures net niet helemaal, Ruby wel (alhoewel daar ook over gediscussieerd wordt) en Groovy zou 'true closures' supporten. Het kenmerkende van closures is dat ze lexicale scope hebben, wat betekent dat ze de variabelen in de omringende scope moeten kunnen gebruiken.

De implementatie in een compiler is lastig te realiseren: echte closures moeten namelijk ook de variabelen in de omringende scope (de methode) kunnen aanpassen. Lokale classes kunnen dit echter ook niet, dus deze vertaling zou op zich wel vrij eenvoudig geimplementeerd kunnen worden. Echte closures implementeren is een stuk lastiger.

Het wordt nog uitdagender als je ook closures kan opleveren: deze code moet ook toegang hebben tot de lokale variabelen van een methode, maar het stack frame van die methode kan allang verloren zijn gegaan als de closure wordt uitgevoerd. Je moet de toestand van lokale variabelen dan op de heap gaan opslaan.

Voorbeeld: GCC implementeert voor C een uitbreiding met geneste functies. Deze functies hebben lexical scope, maar als je een pointer naar zo'n lokale functie returned, gaat het helemaal mis. GCC zorgt er niet voor dat de stack frames in deze situatie op de heap worden geplaatst.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Boktor: Wie kan mij verder op weg helpen? Ik krijg het absoluut niet voor elkaar ;(
Ik begrijp de code niet, misschien omdat de code niet helemaal compleet is, of misschien doordat ik je ontwerp niet snap. Zonder zelf te gaan spelen met de code valt hier weinig over te zeggen (maar dat had je al door dankzij het aantal reacties vandaag). Ik zou even verder proberen uit te zoeken waarom hij blijft hangen. Misschien een cycle in het afvuren van events?

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
mbravenboer schreef op 31 augustus 2004 @ 19:37:
[...]

Ik begrijp de code niet, misschien omdat de code niet helemaal compleet is, of misschien doordat ik je ontwerp niet snap. Zonder zelf te gaan spelen met de code valt hier weinig over te zeggen (maar dat had je al door dankzij het aantal reacties vandaag). Ik zou even verder proberen uit te zoeken waarom hij blijft hangen. Misschien een cycle in het afvuren van events?
Ja idd, was niet zo handig om zo'n lap code te posten. Was meer uit frustratie, omdat ik uren met dit probleem bezig ben geweest.

In JobList staat de volgende stuk code:
Java:
1
2
3
4
5
6
for (int i=0;i<_jobList.size();i++) { 
            JobThread job = _jobList.get(i); 
            if (amount >= Setup.getAmount(currentTime-job.getJob().getLastInvoked(),job.getJob().getPriority())) { 
                _jobList.add(i, jobThread); 
            } 
        } 


hier ontstaat een oneindige lus, nadat de if statement geldt, moet de for-lus afgebroken worden |:(

Ik ben nu dus wel een stuk verder, maar ik krijg hoe langer hoe meer een hekel aan threads :/

Maar als je suggesties hebt voor een beter ontwerp, dan hoor ik het graag :) Ik leer graag wat bij van jullie java-goeroe's ;)

Zoals je misschien al wel hebt gezien wil ik een linkedlist hebben van threads, wat een joblist moet voorstellen uit een besturingsysteem. De bovenste job pak ik op, resume ik, en na een x aantal 'cycli' wordt hij gesuspend en terug geplaatst in de list op basis van de prioriteit van de job.

Normaal gesproken geen probleem, maar het valt me heel zwaar tegen nu er geen 'gewone' objecten in worden geplaatst, maar threads.

[ Voor 20% gewijzigd door JeroenTheStig op 31-08-2004 20:21 ]


Verwijderd

mbravenboer schreef op 31 augustus 2004 @ 19:29:
[...]

Off-topic wordt het meestal pas echt leuk ;)
inderdaad :)

Interesant stukje over closures wat je schreef. Een dergelijke functionaliteit zou in Java zeker niet misstaan, hoewel het natuurlijk wel bekend is dat de ontwerpers van Java het liefst alles als class willen zien. (zie bv de SUN vs MS flames inzake delagates). Closures zou wat dat betreft waarschijnlijk niet boven aan de wish list van de Java designers staan. Het garbage collection mechanisme van Java zou het copieren van stack naar heap wellicht wel wat eenvoudiger maken. In talen als C++ zul je bij het gebruik van meerdere closures heel goed moeten uitkijken wanneer de pointer naar de 'pseudo stack' gedelete mag worden.

Iets wat ook wel lijkt op dit concept (maar dan andersom) zijn lightweight functions, ook wel refinements genoemt. Deze kunnen net zoals een normale functie apart worden gedefineerd, maar op het moment van aanroepen wordt er geen aparte stack voor gemaakt, maar heeft de functie rechtstreeks toegang tot de stack van de aanroepende functie. Feitelijk heb je dan een 'named statement', het heeft in het gebruik iets weg van een macro eigenlijk. Ik kan ze me nog heel vaag herinneren van de taal ABC.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
henk_DE_man: Interesant stukje over closures wat je schreef. Een dergelijke functionaliteit zou in Java zeker niet misstaan, hoewel het natuurlijk wel bekend is dat de ontwerpers van Java het liefst alles als class willen zien.
De Pizza Compiler (werk van met name Martin Odersky and Philip Wadler) ondersteunt naast Generics ook First Class Functions. Eigenlijk was dit een fanastische feature, maar om een of andere reden hebben alleen Generics het ver geschopt in Java zelf. First Class Functions in Pizza hebben lexicale scope en als ik het goed heb kunnen ze ook lokale variabelen aanpassen. De vertaling hiervan is beschreven in de papers over Pizza. Ze gebruiken daar speciale classes voor Closures van lokale variabelen. Deze functionaliteit kan namelijk niet direct vertaald worden naar Java Bytecode, anders hadden inner classes het waarschijnlijk wel ondersteunt.

Pizza Tutorial:
http://pizzacompiler.sourceforge.net/doc/tutorial.html
Iets wat ook wel lijkt op dit concept (maar dan andersom) zijn lightweight functions
Ik heb nooit met een taal gewerkt die dit ondersteunt. Klinkt idd als een macro achtige faciliteit, wat me toch wel wat duidelijker lijkt. Je zou het ook kunnen vergelijken met functions (die optioneel worden ingelined) met dynamische scope. Creepy ;) .

edit:
link naar wikipedia en vrij essentiele typo ;)

[ Voor 6% gewijzigd door mbravenboer op 31-08-2004 22:42 ]

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 02:25
Hee, mijn topic is gekaapt :+

Maar toch nog even een vraagje:

De volgende code staat in de run-methode van een runnable object. Het probleem is dat ik enkele keren de SwingUtilities.invokeLater moet aanroepen om de GUI te refreshen. Het probleem is nu dat de code zoals hij hier staat, niet in deze volgorde wordt afgehandeld. Dat komt dan waarschijnlijk omdat er in deze code ook weer twee nieuwe threads (invokeLater) worden aangemaakt. Hoe kan ik er voor zorgen dat de code op volgorde wordt afgehandeld, maar dat de GUI niet bevriest?

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
while (true) {

            final JobThread thread = _jobListModel.dequeue();
            System.out.println("dequeue " + thread);
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    _selectorLoggingPane.addString("context switch to " + thread.toString());
                }
            });

            thread.resume();
            try {
                Thread.sleep(Settings.CONTENT_SWITCH * Settings.CPU_CYCLE_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
            thread.suspend();

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    if (thread.getJob().isReady()) {
                        _selectorLoggingPane.addString("Job added to readylist");
                        //todo: in readylist knallen
                        System.out.println(thread + " is ready!");
                    } else {
                        _jobListModel.enqueue(thread);
                        System.out.println("enqueue " + thread);
                    }
                }
            });
        }


De volgende output krijg ik in de console. Hier is duidelijk te zien dat enqueue en dequeue niet altijd na elkaar worden aangeroepen.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enqueue job 5: priority 1;121 cpu cycles and 234 hdd cycles left.
dequeue job 5: priority 1;121 cpu cycles and 234 hdd cycles left.
enqueue nullprocess
dequeue nullprocess
enqueue job 5: priority 1;81 cpu cycles and 234 hdd cycles left.
enqueue nullprocess
dequeue job 5: priority 1;81 cpu cycles and 234 hdd cycles left.
dequeue nullprocess
enqueue job 5: priority 1;41 cpu cycles and 234 hdd cycles left.
dequeue job 5: priority 1;41 cpu cycles and 234 hdd cycles left.
enqueue nullprocess
dequeue nullprocess
enqueue job 5: priority 1;1 cpu cycles and 234 hdd cycles left.
dequeue job 5: priority 1;1 cpu cycles and 234 hdd cycles left.
enqueue nullprocess
dequeue nullprocess

Verwijderd

Volgorde en threads gaan niet samen. Ik raad je toch echt aan om dat artikel te lezen over threaded swing programmatie wat ik eerder al postte. Je benadering wat betreft threaded programmeren is gewoon fout.

  • jAnO!
  • Registratie: Januari 2002
  • Laatst online: 01-05 18:22

jAnO!

lalalavanillevla

Verwijderd schreef op 01 september 2004 @ 17:56:
Volgorde en threads gaan niet samen. Ik raad je toch echt aan om dat artikel te lezen over threaded swing programmatie wat ik eerder al postte. Je benadering wat betreft threaded programmeren is gewoon fout.
Idd.

Bovendien zijn resume() en suspend() depricated.
Gebruik wait() en notify() die krijg je gratis en voor niets :*) van Object mee.

Zie:
http://java.sun.com/j2se/...PrimitiveDeprecation.html

When some people work at a place for ten years they get ten years of experience, other people work at a place for ten years and get one year of experience ten times.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Ik zal alles even kortsluiten: dit is een opdracht waarin het klassieke consumer producer probleem opgelost moet worden.

En verder vind ik het persoonlijk een stuk handiger om met de concurrency libraries van doug lea te werken (waarin oa ook semaforen zitten).

[ Voor 38% gewijzigd door Alarmnummer op 01-09-2004 21:07 ]


Verwijderd

Boktor schreef op 01 september 2004 @ 15:17:
Dat komt dan waarschijnlijk omdat er in deze code ook weer twee nieuwe threads (invokeLater) worden aangemaakt.
Mischien even voor de duidelijkheid, invokeLater maakt geen nieuwe threads aan, maar scheduled aan call naar jouw object voor de main event thread ( de 'gui' thread).

Feitelijk wordt er voor jouw object een event aangemaakt (een InvocationEvent) die op de event queue wordt gezet. De offciele naam voor 'gui' thread is dan ook eigenlijk dispatch thread of the event queue. Alle events die zich voor dat tijdstip nog in de queue bevonden worden eerst geprocessed voor dat jouw event afgehandeld wordt (wat resulteerd in een call naar de run functie van het object dat je meegaf).

Heel veel frameworks en toolkits werken met een dergelijke event loop.

Verwijderd

mbravenboer schreef op 31 augustus 2004 @ 20:56:
[...]
Je zou het ook kunnen vergelijken met functions (die optioneel worden ingelined) met dynamische scope. Creepy ;) .
Inderdaad creepy als je ze gaat hergebruiken op verschillende plekken. Ik zeg niet dat het op een goed design wijst of dat refinements opzich iets goeds is, maar de bedoeling van refinements is om ze slechts eenmalig te gebruiken voor een stukje code wat geen zelfstandige grotere betekenis heeft (de som van de statements is niets iets groters dan de statements zelf). Je gebruikt ze dus alleen om een enkele functie op te splitsen in kleinere delen, bv om de leesbaarheid te vergroten. In deze hoedanigheid zijn ze maar iets bruikbaarder dan macro's. De compiler kan nu wel een preciese melding geven als je een functie op een plek gebruikt waar dat niet mag, terwijl een macro helemaal niet meer gezien wordt door de compiler.
Pagina: 1