0

I am just toying around with the idea of creating a multithreaded renderer in swing for my java2d games where each thread is responsible for rendering its own swingcomponent and came up with a simple program to try and and achieve this.

*I am aware that Thread.sleep is not the preferred method but it has worked without a hitch on my single-threaded renderings that use active rendering, I haven't tested with a swingtimer but to my knowledge Thread.sleep sleeps the calling thread so that cannot be the issue.

(Problem) The program creates four panels in four threads with a bouncing ball in each but only the first created thread ever does anything, the others are simply not started at all (verifiable with sysout in run methods). So only one thread does the rendering, the others are never given a chance to run, the sysout: System.out.println("Ballbouncer: " + this + " running."); confirms this and so does the visual (image added).

BallBouncer (runnable)

public class BallBouncer implements Runnable {
private ColoredPanel ballContainer;
private List<Ellipse2D.Double> balls;
public static final Random rnd = new Random();
private double speedX, speedY;

public BallBouncer(ColoredPanel container) {
    this.ballContainer = container;
    this.balls = new ArrayList<>();
    balls.add(container.getBall());
    this.speedX = 10 * rnd.nextDouble() - 5;
    this.speedY = 10 * rnd.nextDouble() - 5;
}

public BallBouncer(List<ColoredPanel> containers) {
    for (ColoredPanel p : containers) {
        new BallBouncer(p).run();
    }
}

@Override
public void run() {
    while (true) {
        System.out.println("Ballbouncer: " + this + " running.");
        moveBall();
        ballContainer.repaint();
        try {
            Thread.sleep(15);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void moveBall() {
    for (Ellipse2D.Double ball : balls) {
        ball.x += speedX;
        ball.y += speedY;

        if (ball.x < 0
                || ball.x + ball.getWidth() > ballContainer.getWidth()) {
            speedX *= -1;
        }
        if (ball.y < 0
                || ball.y + ball.getHeight() > ballContainer.getHeight()) {
            speedY *= -1;
        }
    }


}

Container

public class ColoredPanel extends JPanel {
private Ellipse2D.Double circle;

public ColoredPanel(Color color) {
    circle = new Ellipse2D.Double(0, 0, 10, 10);
    setBackground(color);
}

public Ellipse2D.Double getCircle() {
    return circle;
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.setColor(getBackground().darker());
    g2d.fill(circle);
}

@Override
@Transient
public Dimension getPreferredSize() {
    return new Dimension(400, 400);
}

public Double getBall() {
    return getCircle();
}

Main

public class ColoredPanelContainer extends JPanel {

private List<ColoredPanel> panels = new ArrayList<>();

public ColoredPanelContainer() {
    setUpPanels();
    setBackground(Color.black);
}

private void setUpPanels() {
    for (int i = 0; i < 4; i++) {
        Color color = new Color(BallBouncer.rnd.nextInt(256),
                BallBouncer.rnd.nextInt(256), BallBouncer.rnd.nextInt(256));
        panels.add(new ColoredPanel(color));
    }
    for (int i = 0; i < 4; i++) {
        add(panels.get(i));
    }
}

@Override
@Transient
public Dimension getPreferredSize() {
    return new Dimension(1000, 1000);
}

public List<ColoredPanel> getPanels() {
    return panels;
}

public static void main(String[] args) {
    JFrame frame = new JFrame();
    ColoredPanelContainer container = new ColoredPanelContainer();
    frame.getContentPane().add(container);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    new BallBouncer(container.getPanels());
}
}

Notice the ball only bouncing in the left panel (first started thread), the others are stationary at all times. enter image description here

arynaq
  • 6,710
  • 9
  • 44
  • 74

1 Answers1

4

You're doing this:

public BallBouncer(List<ColoredPanel> containers) {
    for (ColoredPanel p : containers) {
        new BallBouncer(p).run();
    }
}

Which is not the proper way to start a thread. All you're doing is running the run method directly and sequentially. That means that the code runs in that loop in the same thread that calls the constructor.

You should read this tutorial. It explains how to use threads in Swing. Namely, how to use javax.swing.SwingWorker and SwingUtilities.invoke*. There is also this tutorial, which explains how to use the Swing Timer class.

And, just for further education about threads: Here are ways to start threads in java when you're not using swing. You do not want to use these examples when you're writing a Swing application

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • Ah thank you wrapping the runnable in a new Thread and starting it makes the ball bounce in all four panels, I was under the impression that calling .run on a runnable would start it as a new thread. I am slightly nervous when it comes to swing and the EDT, I first dived into swing when trying to create a game and active rendering was much more convinient and powerful for my needs. I have yet to see a good explanation on what the EDT does when you resort to active renderings, I know some java2d games use multithreading with active rendering and few of them rely on swing concurrency. – arynaq Jun 04 '13 at 00:35
  • @arynaq as I said in bold, you don't want to do what you just did. Use a SwingWorker or a Timer instead, depending on your needs. – Daniel Kaplan Jun 04 '13 at 02:20
  • Thanks I am aware, this works for me until I can manage to convince myself fully that it should be not done, looking for a detailed resource on swing concurrency, the tutorial is nice and all but it doesn't explain how stuff is built and thus how I might manage to break it. I don't even know how the rendering is done in the background, will it matter if I spawn a thousand threads on a thousand cores if java has one thread doing the actual rendering to the graphics pipeline? Things I don't know yet! Thanks anyway for the warning. – arynaq Jun 04 '13 at 03:00