0

I simplified my code to share it with you guys. Right now it has 3 classes. JFrame, JInternalFrame and a Thread. JFrame creates the JInternalFrame and the JInternalFrame creates the Thread. The constructor in my thread needs an instance of the JInternalFrame to mess around with the buttons of it. So after I create the interface of my InternalFrame I create and start my Thread. Unluckily the whole JFrame freezes and the code will completely remain in the thread. I already tried to get rid of the while(true) loop in my thread. Then the JFrame freezes only for the amount of time that my sleep function needs.

Long story short: How can I let my thread actually run paralell? Without my main code waiting for any sleep times in the thread or stuff like that.

Here are my 3 classes. Sorry in advance for any stupid noob mistakes. I am still learning.

JFrame

package nachgestellt;

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class Langtons extends JFrame implements ActionListener {
    private JDesktopPane desk;
    private JPanel panelButtons;
    JMenuBar jmb;
    JMenu file;
    JMenuItem open, exit;
    JSlider slider;
    int xInt, yInt;

    Random randomGenerator = new Random();

    JLabel xLabel, yLabel, speed, test;
    JButton start, stop, addAnt;
    JTextField xField, yField;

    public Langtons() {
        //Desktop
        desk = new JDesktopPane();
        getContentPane().add(desk, BorderLayout.CENTER);

        xField = new JTextField();
        yField = new JTextField();

        start = new JButton("Fenster erstellen");

        panelButtons = new JPanel();
        panelButtons.setLayout(new GridLayout());
        ;

        panelButtons.add(start);
        panelButtons.add(xField);
        panelButtons.add(yField);

        start.addActionListener(this);
        add(panelButtons, BorderLayout.NORTH);
        setSize(new Dimension(1100, 900));
        setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("Fenster erstellen")) {
            xInt = Integer.parseInt(xField.getText());
            yInt = Integer.parseInt(yField.getText());
            addChild(new kind(this, xInt, yInt), this.getSize().width, this.getSize().height);
        }
    }


    public void addChild(JInternalFrame kind, int x, int y) {
        kind.setSize(370, 370);
        kind.setLocation(randomGenerator.nextInt(x - kind.getSize().height), randomGenerator.nextInt(y - kind.getSize().height - 100));
        kind.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
        desk.add(kind);
        kind.setBackground(Color.blue);
        kind.setVisible(true);

    }

    public static void main(String[] args) {
        new Langtons();
    }

}

JInternalFrame

package nachgestellt;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;

import javax.swing.*;


public class kind extends JInternalFrame {

    Langtons hFenster;
    static int nr = 0;

    JPanel panelButtonsKind;

    ArrayList<JButton> jbArray = new ArrayList<JButton>();

    JPanel panelSpielfeld;

    int posAmeise, aktuellesIcon;

    public kind(Langtons la, int x, int y) {
        super("Kind " + (++nr), true, true, true, true);
        hFenster = la;

        panelSpielfeld = new JPanel();

        panelSpielfeld.setLayout(new GridLayout(y, x));

        jbArray.add(new JButton());

        for (int i = 1; i <= (x * y); i++) {
            jbArray.add(new JButton(Integer.toString(i)));
            panelSpielfeld.add(jbArray.get(i));
            jbArray.get(i).setBackground(Color.WHITE);
        }

        jbArray.get(((x / 2) * y) - y / 2).setBackground(Color.GREEN);

        posAmeise = (((x / 2) * y) - y / 2);

        setLayout(new BorderLayout());

        panelButtonsKind = new JPanel();
        panelButtonsKind.setLayout(new GridLayout(1, 3));

        add(panelButtonsKind, BorderLayout.NORTH);
        add(panelSpielfeld, BorderLayout.CENTER);
        setVisible(true);

        this.repaint();
        thread a = new thread(this);
        a.start();
        System.out.println("Test Message After Creation and Start of Thread");
    }

    public void changeColor() {
        if (jbArray.get(posAmeise).getBackground().equals(Color.GREEN))
            jbArray.get(posAmeise).setBackground(Color.WHITE);
        else
            jbArray.get(posAmeise).setBackground(Color.GREEN);
    }
}

Thread

package nachgestellt;

import java.awt.Color;

public class thread extends Thread {

    kind k;

    public thread(kind kk) {
        this.k = kk;
        run();
    }

    public void run() {
        super.run();

        while (true) {
            if (k.jbArray.get(k.posAmeise).getBackground().equals(Color.GREEN)) {
                try {
                    sleep(1000);

                    k.changeColor();
                    System.out.println("Test Message Thread!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            } else if (k.jbArray.get(k.posAmeise).getBackground()
                    .equals(Color.WHITE)) {
                try {
                    sleep(1000);
                    k.changeColor();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
Slava Vedenin
  • 49,939
  • 13
  • 36
  • 57
Cappuccino90
  • 259
  • 1
  • 6
  • 24
  • Also see http://stackoverflow.com/questions/29745778/java-game-loop-painting-freezes-my-window/29837148#29837148 – Radiodef May 06 '15 at 22:47

2 Answers2

4

Swing is both single threaded and NOT thread safe, you should never do anything in the Event Dispatching Thread which may block it or run for a long period of time. Equally, you should never try and update the UI from outside the EDT.

See Concurrency in Swing for more details.

Thread#run will simply call the Thread's run method within the context of the current thread, which isn't particularly helpful, you should be using Thread#start.

However, having said that, in your case, you should use a Swing Timer instead of your Thread. The timer will setup a callback to the registered ActionListener at the specified delay interval, which is executed within the context of the EDT, making it safe to update the UI from within.

See How to use Swing Timers for more details

MadProgrammer
  • 323,026
  • 21
  • 204
  • 329
3

The most immediate issues in your code are:

public thread(kind kk) {
    this.k = kk;
    run();
}
  • You don't want to invoke the run() method directly. You want to call start() on a thread.
  • You shouldn't start a thread from within a constructor (this can lead to problems)
  • You should probably find a better name for this class than lowercase thread. (Not a bug per say, just poor naming).

Also, within your run method, you have:

public void run() {
    super.run();
}

The super invocation is unnecessary, and you should add an @Override annotation to improve the clarity of your code:

@Override
public void run() {
   //your code
}
Amir Afghani
  • 35,568
  • 16
  • 81
  • 120
  • 1
    Thank you very much kind sir ! Taking the .start out of my thread constructor did the magic. Of course I also took care of your other advises :) – Cappuccino90 May 08 '15 at 21:19