Threads Liste In unterschiedlichen Threads bearbeiten

Viktim

Bekanntes Mitglied
Hi,

Ich habe momentan das Problem, dass ich eine Liste in 2 Unterschiedlichen Threads bearbeiten muss.
Der eine schmeißt alle 100 Millisekunden ein Objekt rein und der Andere sorgt dafür das Permanent was damit passiert.
Dabei kriege ich warum auch immer eine ArrayIndexOutOfBoundsException 🙁
Leider wird im Stacktrace keine von mir geschriebene Klasse erwähnt, sodass ich nicht weiß wo der Fehler ist.

Code:
Java:
  public void starteSpiel() {
    hindernisse = Collections.synchronizedList(new ArrayList<>());

    Runnable bewege = () -> {
      while (true) {
        synchronized (hindernisse) {
          for (Hindernis hindernis : hindernisse) {
            hindernis.setzteAufNaechstePosition();
          }
        }
      }
    };
    Thread bewegeHindernisse = new Thread(bewege);
    bewegeHindernisse.setDaemon(true);
    bewegeHindernisse.start();

    @SuppressWarnings("static-access")
    Runnable baue = () -> {
      while (true) {
        try {
          synchronized (hindernisse) {
            hindernisse.add(new Hindernis(hintergrund));
          }
          Thread.currentThread().sleep(zeitZumnachstenHindernis);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }

      }
    };
    Thread baueHindernisse = new Thread(baue);
    baueHindernisse.setDaemon(true);
    baueHindernisse.start();
  }

Stacktrace:
Java:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: No such child: 8
   at java.awt.Container.getComponent(Container.java:334)
   at javax.swing.JComponent.rectangleIsObscured(JComponent.java:4390)
   at javax.swing.JComponent.paint(JComponent.java:1054)
   at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)
   at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290)
   at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
   at javax.swing.JComponent._paintImmediately(JComponent.java:5158)
   at javax.swing.JComponent.paintImmediately(JComponent.java:4969)
   at javax.swing.RepaintManager$4.run(RepaintManager.java:831)
   at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
   at java.security.AccessController.doPrivileged(Native Method)
   at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
   at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
   at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
   at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
   at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
   at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
   at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
   at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
   at java.awt.EventQueue.access$500(EventQueue.java:97)
   at java.awt.EventQueue$3.run(EventQueue.java:709)
   at java.awt.EventQueue$3.run(EventQueue.java:703)
   at java.security.AccessController.doPrivileged(Native Method)
   at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
   at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
   at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
   at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
   at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
   at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
   at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

EDIT: Wenn ihr mehr Code braucht, sagt bescheid
 
Verändern beide Threads die Oberfläche ?
Wenn ja, dann liegt dein Problem darin begründet, dass Swing damit nicht zurecht kommt. Bei einem Swing Programm darf es nur einen Thread geben, der auf die Oberfläche zugreift.
 
Swing.... 😡

Aber danke für die Antwort 😀 jetzt kann ich eigentlich alles Wegwerfen, weil ohne Threads klappt das nicht 🙁

EDIT: Das ist ja der Post der Stimmungsschwankungen 😀
 
Da tritt leider Das gleiche Problem auf 🙁
Java:
  public void starteSpiel() {
    hindernisse = Collections.synchronizedList(new ArrayList<>());

    SwingWorker<Boolean, List<Hindernis>> worker = new SwingWorker<Boolean, List<Hindernis>>() {

      @Override
      protected Boolean doInBackground() throws Exception {
        while (true) {
          synchronized (hindernisse) {
            for (Hindernis hindernis : hindernisse) {
              hindernis.setzteAufNaechstePosition();
            }
          }
        }
      }
    };
    worker.execute();

    SwingWorker<Boolean, List<Hindernis>> worker1 = new SwingWorker<Boolean, List<Hindernis>>() {
      @Override
      protected Boolean doInBackground() throws Exception {
        while (true) {
          try {
            synchronized (hindernisse) {
              hindernisse.add(new Hindernis(hintergrund));
            }
            Thread.currentThread().sleep(zeitZumnachstenHindernis);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }

        }
      }
    };
    worker1.execute();
  }
 
Es ist das Gegenteil: Du rufst irgendwo ein repaint auf und löscht aber eine Componente während des Neuzeichnens. Dann hast du natürlich eine ArrayIndexOutOfBoundsException
 
Du rufst irgendwo ein repaint auf und löscht aber eine Componente während des Neuzeichnens

Löschen tuh ich eigentlich nichts... 😵 ich füg aber mittlerweile ein Element hinzu 😀 ich poste die anderen Methoden auch mal 😀
Java:
public class Hindernis extends BeweglichesObjekt {

  /**
   *
   */
  private static final long serialVersionUID = 1L;

  private int xSchritte = (int) (Math.random() * 10);
  private int ySchritte = (int) (Math.random() * 10);
  private int achse;

  public Hindernis(Hintergrund hintergrund) {
    this.hintergrund = hintergrund;
    setzteWerte();
    ImageIcon picture = new ImageIcon("spaceIcon.png");
    picture.setImage(picture.getImage().getScaledInstance(breite, hoehe, Image.SCALE_REPLICATE));
    setIcon(picture);
    setPreferredSize(new Dimension(breite, hoehe));
  }

  private void setzteWerte() {
    int groesse = (int) (Math.random() * 150) + 1;
    breite = groesse;
    hoehe = groesse;
    double rateZahlenwert = Math.random();
    if (rateZahlenwert < 0.25) {
      xPos = 0;
      yPos = (int) (Math.random() * hintergrund.getHeight());
      achse = 0;
    } else if (rateZahlenwert < 0.5 && rateZahlenwert >= 0.25) {
      xPos = hintergrund.getWidth() - breite;
      yPos = (int) (Math.random() * hintergrund.getHeight());
      achse = 1;
    } else if (rateZahlenwert < 0.75 && rateZahlenwert >= 0.5) {
      xPos = (int) (Math.random() * hintergrund.getWidth());
      yPos = 0;
      achse = 2;
    } else if (rateZahlenwert >= 0.75) {
      xPos = (int) (Math.random() * hintergrund.getWidth());
      yPos = hintergrund.getWidth() - breite;
      achse = 3;
    }
  }

  public void setzteAufNaechstePosition() {
    switch (achse) {
      case 0:
        xPos += xSchritte;
        int richtung = (int) Math.random();
        if (richtung > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 1:
        xPos -= xSchritte;
        int richtung1 = (int) Math.random();
        if (richtung1 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 2:
        yPos += ySchritte;
        int richtung2 = (int) Math.random();
        if (richtung2 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 3:
        xPos -= xSchritte;
        int richtung3 = (int) Math.random();
        if (richtung3 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
    }
    bewegen();
  }
}

Java:
public abstract class BeweglichesObjekt extends JLabel {
  /**
   *
   */
  private static final long serialVersionUID = 1L;
  protected Hintergrund hintergrund;
  protected int hoehe = 30;
  protected int breite = 30;
  protected int xPos = 0;
  protected int yPos = 0;

  protected static BufferedImage rotateImage(BufferedImage src, double degrees) {

    AffineTransform affineTransform = AffineTransform.getRotateInstance(
        Math.toRadians(degrees),
        src.getWidth() / 2,
        src.getHeight() / 2);
    BufferedImage rotatedImage = new BufferedImage(src.getWidth(), src
        .getHeight(), src.getType());
    Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
    g.setTransform(affineTransform);
    g.drawImage(src, 0, 0, null);
    return rotatedImage;
  }

  @Override
  public int getHeight() {
    return hoehe;
  }

  @Override
  public int getWidth() {
    return breite;
  }

  protected void bewegen() {
    hintergrund.add(this, xPos, yPos, breite, hoehe);
    hintergrund.refreshe();
  }

  public int getxPos() {
    return xPos;
  }

  public void setxPos(int xPos) {
    this.xPos = xPos;
  }

  public int getyPos() {
    return yPos;
  }

  public void setyPos(int yPos) {
    this.yPos = yPos;
  }

Java:
public class Hintergrund extends JPanel {

  /**
   *
   */
  private static final long serialVersionUID = 1L;
  private BufferedImage img;

  public Hintergrund() {
    setLayout(null);
    try {
      img = ImageIO.read(new File("spaceHintergrund.jpg"));
    } catch (IOException e) {
      System.out.print("Bild nicht gefunden");
    }
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
  }

  public void add(JComponent component, int x, int y, int breite, int hoehe) {
    add(component);
    component.setBounds(x, y, breite, hoehe);
  }

  public void refreshe() {
    repaint();
    validate();
  }

}
 
Okay 😀

Die Main:
Java:
public class ThreadSpiel {
  public static void main(String[] args) {
    Fenster fenster = new Fenster("Super Thread Tests");
    fenster.baueFeld();
    fenster.fuegeSpielerHinzu();
    fenster.starteSpiel();
  }
}

Der JFrame:
Java:
public class Fenster extends JFrame {

  /**
   *
   */
  private static final long serialVersionUID = 1L;
  private Hintergrund hintergrund;
  private int zeitZumnachstenHindernis = 100;
  private List<Hindernis> hindernisse;

  public Fenster(String fensterName) {
    super(fensterName);
    setSize(500, 500);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }

  public void baueFeld() {
    hintergrund = new Hintergrund();
    add(hintergrund);
    refresh();
  }

  public void fuegeSpielerHinzu() {
    Spieler spieler1 = new Spieler(hintergrund);
    hintergrund.add(spieler1, spieler1.getxPos(), spieler1.getyPos(), spieler1.getWidth(), spieler1.getHeight());
    addKeyListener(new Spielerverhalten(spieler1));
    refresh();
  }

  public void starteSpiel() {
    hindernisse = Collections.synchronizedList(new ArrayList<>());

    SwingWorker<Boolean, List<Hindernis>> worker = new SwingWorker<Boolean, List<Hindernis>>() {

      @Override
      protected Boolean doInBackground() throws Exception {
        while (true) {
          synchronized (hindernisse) {
            for (Hindernis hindernis : hindernisse) {
              hindernis.setzteAufNaechstePosition();
            }
          }
        }
      }
    };
    worker.execute();

    SwingWorker<Boolean, List<Hindernis>> worker1 = new SwingWorker<Boolean, List<Hindernis>>() {
      @Override
      protected Boolean doInBackground() throws Exception {
        while (true) {
          try {
            synchronized (hindernisse) {
              hindernisse.add(new Hindernis(hintergrund));
            }
            Thread.currentThread().sleep(zeitZumnachstenHindernis);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }

        }
      }
    };
    worker1.execute();
  }

  public void refresh() {
    repaint();
    validate();
  }

}

Der Hintergrund bzw das Panel:
Java:
public class Hintergrund extends JPanel {

  /**
   *
   */
  private static final long serialVersionUID = 1L;
  private BufferedImage img;

  public Hintergrund() {
    setLayout(null);
    try {
      img = ImageIO.read(new File("spaceHintergrund.jpg"));
    } catch (IOException e) {
      System.out.print("Bild nicht gefunden");
    }
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
  }

  public void add(JComponent component, int x, int y, int breite, int hoehe) {
    add(component);
    component.setBounds(x, y, breite, hoehe);
  }

  public void refreshe() {
    repaint();
    validate();
  }

}
Der Spieler:
Java:
public class Spieler extends BeweglichesObjekt {
  /**
   *
   */
  private static final long serialVersionUID = 1L;
  private final int SCHRITTWEITE = 5;

  public Spieler(Hintergrund hintergrund) {
    ImageIcon picture = new ImageIcon("spaceSpieler.png");
    picture.setImage(picture.getImage().getScaledInstance(breite, hoehe, Image.SCALE_REPLICATE));
    setIcon(picture);
    setPreferredSize(new Dimension(breite, hoehe));
    this.hintergrund = hintergrund;
  }

  public void hoch() {
    ImageIcon picture = new ImageIcon("spaceSpieler.png");
    picture.setImage(picture.getImage().getScaledInstance(breite, hoehe, Image.SCALE_REPLICATE));
    setIcon(picture);
    if (yPos - SCHRITTWEITE < 0) {
      yPos = hintergrund.getHeight() - hoehe;
    } else {
      yPos -= SCHRITTWEITE;
    }
    bewegen();
  }

  public void runter() {
    dreheIcon(180);
    if (yPos + SCHRITTWEITE > hintergrund.getHeight()) {
      yPos = 0;
    } else {
      yPos += SCHRITTWEITE;
    }
    bewegen();
  }

  public void links() {
    dreheIcon(270);
    if (xPos - SCHRITTWEITE < 0) {
      xPos = hintergrund.getWidth() - breite;
    } else {
      xPos -= SCHRITTWEITE;
    }
    bewegen();
  }

  public void rechts() {
    dreheIcon(90);
    if (xPos + SCHRITTWEITE > hintergrund.getWidth()) {
      xPos = 0;
    } else {
      xPos += SCHRITTWEITE;
    }
    bewegen();
  }

  private void dreheIcon(double grad) {
    BufferedImage image = null;
    try {
      image = ImageIO.read(new File("spaceSpieler.png"));
    } catch (IOException e) {
      e.printStackTrace();
    }
    image = rotateImage(image, grad);
    ImageIcon picture = new ImageIcon(image);
    picture.setImage(picture.getImage().getScaledInstance(breite, hoehe, Image.SCALE_REPLICATE));
    setIcon(picture);
  }

}

Hindernis:
Java:
public class Hindernis extends BeweglichesObjekt {

  /**
   *
   */
  private static final long serialVersionUID = 1L;

  private int xSchritte = (int) (Math.random() * 10);
  private int ySchritte = (int) (Math.random() * 10);
  private int achse;

  public Hindernis(Hintergrund hintergrund) {
    this.hintergrund = hintergrund;
    setzteWerte();
    ImageIcon picture = new ImageIcon("spaceIcon.png");
    picture.setImage(picture.getImage().getScaledInstance(breite, hoehe, Image.SCALE_REPLICATE));
    setIcon(picture);
    setPreferredSize(new Dimension(breite, hoehe));
  }

  private void setzteWerte() {
    int groesse = (int) (Math.random() * 150) + 1;
    breite = groesse;
    hoehe = groesse;
    double rateZahlenwert = Math.random();
    if (rateZahlenwert < 0.25) {
      xPos = 0;
      yPos = (int) (Math.random() * hintergrund.getHeight());
      achse = 0;
    } else if (rateZahlenwert < 0.5 && rateZahlenwert >= 0.25) {
      xPos = hintergrund.getWidth() - breite;
      yPos = (int) (Math.random() * hintergrund.getHeight());
      achse = 1;
    } else if (rateZahlenwert < 0.75 && rateZahlenwert >= 0.5) {
      xPos = (int) (Math.random() * hintergrund.getWidth());
      yPos = 0;
      achse = 2;
    } else if (rateZahlenwert >= 0.75) {
      xPos = (int) (Math.random() * hintergrund.getWidth());
      yPos = hintergrund.getWidth() - breite;
      achse = 3;
    }
  }

  public void setzteAufNaechstePosition() {
    switch (achse) {
      case 0:
        xPos += xSchritte;
        int richtung = (int) Math.random();
        if (richtung > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 1:
        xPos -= xSchritte;
        int richtung1 = (int) Math.random();
        if (richtung1 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 2:
        yPos += ySchritte;
        int richtung2 = (int) Math.random();
        if (richtung2 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
      case 3:
        xPos -= xSchritte;
        int richtung3 = (int) Math.random();
        if (richtung3 > 0.5) {
          yPos -= ySchritte;
        } else {
          yPos += ySchritte;
        }
        break;
    }
    bewegen();
  }
}

Oberklasse von Spieler und Hindernis:
Java:
public abstract class BeweglichesObjekt extends JLabel {
  /**
   *
   */
  private static final long serialVersionUID = 1L;
  protected Hintergrund hintergrund;
  protected int hoehe = 30;
  protected int breite = 30;
  protected int xPos = 0;
  protected int yPos = 0;

  protected static BufferedImage rotateImage(BufferedImage src, double degrees) {

    AffineTransform affineTransform = AffineTransform.getRotateInstance(
        Math.toRadians(degrees),
        src.getWidth() / 2,
        src.getHeight() / 2);
    BufferedImage rotatedImage = new BufferedImage(src.getWidth(), src
        .getHeight(), src.getType());
    Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
    g.setTransform(affineTransform);
    g.drawImage(src, 0, 0, null);
    return rotatedImage;
  }

  @Override
  public int getHeight() {
    return hoehe;
  }

  @Override
  public int getWidth() {
    return breite;
  }

  protected void bewegen() {
    hintergrund.add(this, xPos, yPos, breite, hoehe);
    hintergrund.refreshe();
  }

  public int getxPos() {
    return xPos;
  }

  public void setxPos(int xPos) {
    this.xPos = xPos;
  }

  public int getyPos() {
    return yPos;
  }

  public void setyPos(int yPos) {
    this.yPos = yPos;
  }
}
Der KeyListener für die Steuerung
Java:
public class Spielerverhalten implements KeyListener {

  private Spieler spieler;

  public Spielerverhalten(Spieler spieler) {
    this.spieler = spieler;
  }

  @Override
  public void keyTyped(KeyEvent e) {
  }

  @Override
  public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_UP) {
      spieler.hoch();
    }
    if (e.getKeyCode() == KeyEvent.VK_DOWN) {
      spieler.runter();
    }
    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
      spieler.links();
    }
    if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
      spieler.rechts();
    }
  }

  @Override
  public void keyReleased(KeyEvent e) {
    // TODO Auto-generated method stub

  }

}

Viel Spaß damit 😀
 
Also dein Übeltäter liegt auf jedenfall bei Hintergrund::add(JComponent, int, int, int, int) mit dem add(component);.

Was passiert genau? Swing und auch FX haben UI-Bäume und müssen genau Buch führen wo welches Element untergeordnet ist, um es zu "layouten" und zeichnen. Hier wird auch ein "remove" auf den alten Elternteil aufgerufen und weil irgendwo gerade ein repaint aufgerufen wird, wird die Änderung nicht schnell genug übernommen -> Exception.

Könnte man draufkommen, wenn man mal den Debugger startet.

Das liegt wiederum am schlechten Design deines Spiels. Ich würd dir mal raten Quaxli's Tutorial durchzugehen, dann siehst du wie das richtig funktionieren soll (oder benutzt gleich eine GameEngine)!
 
Dein Design ist Müll! Du fügst jedes mal das gleiche zu deinem Hintergrund hinzu! Was du aber machen solltest ist, nur neu zeichnen mit den Positionsupdates. Sieh dir das Tutorial an! Dann siehst du wie man das sauber implementiert und wirf das Projekt so wie es ist über den Haufen.
 
Dein Design ist Müll! Du fügst jedes mal das gleiche zu deinem Hintergrund hinzu! Was du aber machen solltest ist, nur neu zeichnen mit den Positionsupdates. Sieh dir das Tutorial an! Dann siehst du wie man das sauber implementiert und wirf das Projekt so wie es ist über den Haufen.
Okay, ich kann die pdf aber nicht öffnen 🙁
 
Heißt das dein Programm läuft? Was hast du geändert, damit die Nachwelt an deiner Lösung teilhaben kann.
 

Zurück
Oben