[java threads] JList refresh gaat fout, sync probleem

Pagina: 1
Acties:

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 19:42
Ik heb een Monitor-object gemaakt waarin een ListModel is opgenomen. Dit model wordt door twee threads benaderd om er iets in te stoppen of iets uit te halen. Thread 1 genereert objecten en deze objecten worden in de monitor geplaatst. Thread twee haalt ene object uit de monitor en plaatst hem na behandeling weer terug. Er zijn dus drie methodes, namelijk een generate(Object o), een put(Object o) en een pop() methode, die alle drie gesynchronizeerd zijn. Hieronder een klein stukje code:

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
public class Monitor
{

  private ListModel _queue;
  private boolean _locked;
  
  public Monitor() 
  {
    // initialise components e.d.
  }

  // wordt aangeroepen door de GUI component
  public ListModel getQueue() 
  {
    return _queue;
  }

  // wordt aangeroepen door Thread 1
  public synchronized boolean generate(Job job) {    
  {
    if (!_locked)
    {
      _buffer.add(job, calculateIndex(job));
      return true;
    }
    return false;
  }

  // wordt aangeroepen door Thread 2
  public synchronized Job pop() {              
  {
     return _buffer.remove(0);
  }

  public synchronized void put(Job job) {   // wordt aangeroepen door Thread 2
  {
    _buffer.add(job ,calculateIndex(job)));
  }

  ...
  ...
}


Om een indicatie te geven hoe de threads, monitor en GUI met elkaar communiceren, heb ik het volgende schema gemaakt:

Afbeeldingslocatie: http://www.xs4all.nl/~stiege/jeroen/GoT/threadprobleem.GIF

De ListModel in de Monitor wordt dus in de GUI weergegeven in een JList. Deze ListModel wordt daarnaast benaderd door thread 1 mbv de 'synchronized boolean generate(Job job)' en door thread 2 mbv de methodes 'synchronized Job pop()' en 'synchronized void put(Job job)'. Dit werkt allemaal prima.

Wat gaat er dan wel fout?

Bij het weergeven van de ListModel gaat het heel af en toe fout. Dit gebeurt vanuit de gui-component, want de fout wordt veroorzaakt bij de ListModel.get(int index) methode. De index komt af en toe buiten zijn bereik:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 10, Size: 10
    at java.util.LinkedList.entry(LinkedList.java:368)
    at java.util.LinkedList.get(LinkedList.java:313)
    at jobSimulator.monitor.MonitorListModel.getElementAt(MonitorListModel.java:31)
    at jobSimulator.monitor.MonitorListModel.getElementAt(MonitorListModel.java:17)
    at javax.swing.plaf.basic.BasicListUI.paintCell(BasicListUI.java:182)
    at javax.swing.plaf.basic.BasicListUI.paint(BasicListUI.java:287)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:142)
    at javax.swing.JComponent.paintComponent(JComponent.java:740)
    at javax.swing.JComponent.paint(JComponent.java:1003)
    at javax.swing.JComponent.paintWithOffscreenBuffer(JComponent.java:4930)
    at javax.swing.JComponent.paintDoubleBuffered(JComponent.java:4883)
    at javax.swing.JComponent._paintImmediately(JComponent.java:4826)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4633)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:451)
    at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:114)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
    at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:234)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)


De enige verklaring die ik kan geven is dat tijdens het refreshen van de JList door Thread 2 een pop wordt gedaan. Daardoor wordt de grootte van de queue 1 object kleiner, waardoor bij het weergeven van het laatste object een IndexOutOfBoundsException optreedt.

Mijn vraag is hoe ik ook het refreshen van de JList kan synchroniseren, zodat op dat moment geen in- of uitvoer op de queue gedaan kan worden?

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

Alarmnummer

-= Tja =-

Boktor schreef op zaterdag 28 mei 2005 @ 11:16:
Ik heb een Monitor-object gemaakt waarin een ListModel is opgenomen.
Ieder java object is al een monitor.
Dit model wordt door twee threads benaderd om er iets in te stoppen of iets uit te halen.
Cool.. threads... * Alarmnummer likes threads
Bij het weergeven van de ListModel gaat het heel af en toe fout. Dit gebeurt vanuit de gui-component, want de fout wordt veroorzaakt bij de ListModel.get(int index) methode. De index komt af en toe buiten zijn bereik:
Regel: als meerdere threads op een bepaalde resource gaan lezen en schrijven dan is synchronisatie vaak vereist (er zijn uitzonderingen maar dat is nu niet belangrijk). In jouw geval kan jouw queue door meerdere threads tegelijk worden aangesproken. Het kan namelijk gebeuren dat jouw gui aan het painten is en de listmodel raadpleegt en dat jouw threads dezelfde listmodel aan het updaten zijn. Jouw synchronisatie zal dat niet gaan verhinderen. De ListModel trouwens volledig gaan synchronizen is ook zinloos. Alhoewel je wel kan uitsluiten dat je structuur niet corrupt kan raken, kunnen clients van de code zeker wel crashen. Ik synchronize bijna nooit methodes...(altijd interne locks die niemand anders dan het object kan aansturen). Op mijn objecten kun je ook bijna nooit een lock krijgen en je zult in hoge mate om moeten gaan met non determinisme in je code..
Mijn vraag is hoe ik ook het refreshen van de JList kan synchroniseren, zodat op dat moment geen in- of uitvoer op de queue gedaan kan worden?
De vraag is of synchronisatie noodzakelijk is. Wat je zou kunnen doen is de access van die lijst volledig via de event dispatching thread te laten verlopen. Jouw gui die gebruikt die juiste thread al, nu hoef je alleen nog maar te zorgen dat jouw threads via de event dispatching thread de wijzigingen uitvoeren. Hierdoor hoef je dus helemaal niets meer te locken.

Opmerkingen:
Ik zou geen ListModel gebruiken als model. Ik zou hiervoor een gewonen List/Queue voor gebruiken. En verder zou ik in dit geval gaan werken met een snapshot van die queue. Als jouw gui wil gaan painten, neemt hij een veilige snapshot (dus gesynchroniseerd) en bouwt op basis hiervan het model op en dan kan je tekenen. Verder zul je ook de gui moeten 'updaten'. Dat kan je doen door een timer te koppelen aan de refresh. Het lijkt misschien nu ingewikkelder.. maar je hebt
1) view en model zijn nu duidelijker van elkaar gescheiden.
2) imho ook een duidelijkere concurrency model.

[ Voor 10% gewijzigd door Alarmnummer op 28-05-2005 11:49 ]