0

I saw a Topic about multi thread here : Multi-threading with Swing

So I do as the answer https://stackoverflow.com/a/33908340/11445086

I implement like codes below but there's not anything movement here. I know the problem is about my doInBackGround and process implementation but I don't know how to do.I'm really new with Swing Builder so sorry if the question's silly. The program just makes many circles moving in the Panel. And each circle is a Thread that be made by Swing Worker.

these're my classes:

Robot class :

package com.mycompany.test;

import java.awt.Color;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;

public class Robot extends SwingWorker< Void ,Integer> {
    public int x;
    public int y;
    public Color color;
    public final int speed = 10;
    Robot(int x , int y , Color color)
    {
        this.x = x;
        this.y = y;
        this.color = color;
    }
    public void move_90()
    {
        this.y += speed;
    }

    public void move_270()
    {
        this.y -= speed;
    }

    public void move_180()
    {
        this.x += speed;
    }

    public void move_0()
    {
        this.x += speed;
    }

    public void move_45()
    {
        this.x += speed;
        this.y += speed;
    }

    public void move_135()
    {
        this.x -= speed;
        this.y += speed;
    }

    public void move_225()
    {
        this.x -= speed;
        this.y -= speed;
    }

    public void move_315()
    {
        this.x += speed;
        this.y -= speed;
    }

    public void move()
    {
        Random temp = new Random();
        int rand = temp.nextInt(8);
        switch(rand)
        {
            case 1: move_0();
            break;
            case 2: move_135();
            break;
            case 3: move_180();
            break;
            case 4: move_225();
            break;
            case 5: move_270();
            break;
            case 6: move_315();
            break;
            case 7: move_45();
            break;
            case 8: move_90();
            break;
        }
    }
    @Override
    protected void process(List<Integer> chunks) {
         while(true)
        {
            move();
            if(x < 40) x = 40;
            if(x > PlayField.width - 40) x = (PlayField.width - 40);
            if(y < 40) y = 40;
            if(y > PlayField.height - 40) y = (PlayField.height - 40);
             try {
                 Thread.sleep(20);
             } catch (InterruptedException ex) {
                 Logger.getLogger(Robot.class.getName()).log(Level.SEVERE, null, ex);
             }
        }
    }

    @Override
    protected Void doInBackground() throws Exception {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}

RobotModel class :

package com.mycompany.test;

import java.awt.Color;
import java.util.LinkedList;

public class RobotModel {
    public static final int MAX = 8;
    public LinkedList<Robot> model = new LinkedList<Robot>();
    public void add_New_Robot()
    {
        Robot temp = new Robot( 40 , 40 , Color.BLUE);
        model.addFirst(temp);
    }
}

PlayField class :

package com.mycompany.test;

import java.awt.Color;

public class PlayField {
    public static int width;
    public static int height;
    public static Color fill_Color;
    PlayField(int width , int height , Color fill_Color)
    {
        this.width = width;
        this.height = height;
        this.fill_Color = fill_Color;
    }
}

RobotWorld class :

package com.mycompany.test;

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;

public class RobotWorld extends JPanel {
    public RobotModel robot_Model;

    public RobotWorld(RobotModel robot_Model) {
        super();
        this.robot_Model = robot_Model;
        this.setSize(PlayField.width , PlayField.height);
        this.setVisible(true);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphic = (Graphics2D)g;
        graphic.setBackground(PlayField.fill_Color);
        for(Robot x : robot_Model.model )
        {
            graphic.setColor(x.color);
            graphic.drawOval(x.x, x.y, 40, 40);
        }
    }

}

And finally , GameMain class :

package com.mycompany.test;

import java.awt.Color;
import javax.swing.JFrame;

public class GameMain extends JFrame {
    RobotModel a;
    PlayField field;
    public void Game_Start()
    {
        Robot robot = new Robot(100, 100, Color.RED);
        a = new RobotModel();
        RobotWorld world = new RobotWorld(a);
        world.robot_Model.add_New_Robot();
        this.setSize(field.width , field.height);
        this.add(world);
        this.setVisible(true);
        world.repaint();
    }

//    public void gameUpdate(Robot a , PlayField field)
//    {
//        a.move();
//        if(a.x < 40) a.x = 40;
//        if(a.x > field.width - 40) a.x = (field.width - 40);
//        if(a.y < 40) a.y = 40;
//        if(a.y > field.height - 40) a.y = (field.height - 40);
//    }


    public void gameUpdate(){
    Thread gameThread = new Thread(){
        public void run(){
            while(true){
                //execute one time step for the game
//                gameUpdate(a , field);

                //refresh screen
                repaint();

                //give other threads time
                try{
                    Thread.sleep(5);
                }catch(InterruptedException e){
//                    e.printStackTrace();
                }
            }
        }
    };

    gameThread.start();
}



    public static void main(String args[])
    {
        GameMain main = new GameMain();
        main.Game_Start();
        main.gameUpdate();
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 3
    I wouldn't use `SwingWorker` for this type of work, apart from the fact that it will only support 10 simultaneous threads, it will decrease the performance of your app as you try and add more. Instead, you should use a Swing `Timer` as your "main" loop. This allow you to update elements that the UI relies on from within the EDT and schedule `repaint`s safely – MadProgrammer Oct 16 '19 at 00:47
  • 1
    It's not doing anything because you never [`execute()`](https://docs.oracle.com/javase/8/docs/api/javax/swing/SwingWorker.html#execute--) the Swing worker. – Andreas Oct 16 '19 at 00:50
  • *Unrelated:* [`Random.nextInt(8)`](https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#nextInt-int-) returns a number between 0 and 7, not a number between 1 and 8. – Andreas Oct 16 '19 at 00:51
  • @Andreas How can I do execute() ? Can you give me a suggestion? – Thành Công Oct 16 '19 at 00:59
  • @Andreas Tks ! I will fix the switch-case code – Thành Công Oct 16 '19 at 00:59
  • 1
    @ThànhCông Suggestion: If you want to try using *Swing Worker*, go through a tutorial teaching you how they work. However, you should follow the [suggestion by MadProgrammer](https://stackoverflow.com/questions/58404268/how-to-make-multi-thread-with-swing-worker?noredirect=1#comment103153528_58404268), and use a **Swing Timer** instead. – Andreas Oct 16 '19 at 01:01
  • @ThànhCông Understand that Swing IS NOT THREAD SAFE. Updating the UI or something the UI relies on from outside the EDT is dangerous and highly not recommend. The `SwingWorker` is not syncing it's updates back to the EDT (via `publish`/`process`) which is the whole point of `SwingWorker`. `SwingWorker` simply isn't well suited to your problem. Replace your `gameThread` with a Swing `Timer` and perform your updates within it instead – MadProgrammer Oct 16 '19 at 01:04
  • @ThànhCông You also do nothing in the `doInBackground` method ... so, really, it's just a lack of understanding in how the API works – MadProgrammer Oct 16 '19 at 01:07

1 Answers1

0

The problems are multiple, mostly to do with a lack of understand over how SwingWorker actually works.

Remember, more threads doesn't always more more work. The more threads you have, the harder the system will have to work. Sometimes, less is more ;)

SwingWorker is not well suited to this task. It will only allow a maximum of 10 workers to execute, for the WHOLE app and, unless you're synchronising your updates with the Event Dispatching Thread properly, you'd not be gaining any benefit from it.

A generally simpler solution would be to make use of a Swing Timer instead. This will ensure your updates are carried out within the EDT without the risk of dirty read/writes or blocking the EDT.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();

                PlayField playField = new PlayField(200, 200, Color.DARK_GRAY);
                RobotModel model = new RobotModel();
                model.createNewRobot();

                RobotWorld world = new RobotWorld(playField, model);
                world.start();

                frame.add(world);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class RobotModel {

        public static final int MAX = 8;
        public LinkedList<Robot> bots = new LinkedList<Robot>();

        public void createNewRobot() {
            Robot temp = new Robot(40, 40, Color.BLUE);
            bots.addFirst(temp);
        }
    }

    public class PlayField {

        public int width;
        public int height;
        public Color fill_Color;

        PlayField(int width, int height, Color fill_Color) {
            this.width = width;
            this.height = height;
            this.fill_Color = fill_Color;
        }
    }

    public class RobotWorld extends JPanel {

        public RobotModel model;
        private PlayField playField;

        private Timer timer;

        public RobotWorld(PlayField playField, RobotModel robot_Model) {
            super();
            this.model = robot_Model;
            this.playField = playField;

            setBackground(playField.fill_Color);

            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {
                    Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
                    for (Robot bot : model.bots) {
                        bot.move(bounds);
                    }
                    repaint();
                }
            });
        }

        public void start() {
            timer.start();
        }

        public void stop() {
            timer.stop();
        }

        @Override
        public Dimension getPreferredSize() {
            return playField == null ? super.getPreferredSize() : new Dimension(playField.width, playField.height);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (Robot bot : model.bots) {
                Graphics2D graphic = (Graphics2D) g.create();
                bot.paint(graphic);
                graphic.dispose();
            }
        }
    }

    public class Robot {

        public int x;
        public int y;
        public Color color;
        public final int speed = 10;
        public int size = 40;

        Robot(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillRect(x, y, size, size);
        }

        public void move_90() {
            this.y += speed;
        }

        public void move_270() {
            this.y -= speed;
        }

        public void move_180() {
            this.x += speed;
        }

        public void move_0() {
            this.x += speed;
        }

        public void move_45() {
            this.x += speed;
            this.y += speed;
        }

        public void move_135() {
            this.x -= speed;
            this.y += speed;
        }

        public void move_225() {
            this.x -= speed;
            this.y -= speed;
        }

        public void move_315() {
            this.x += speed;
            this.y -= speed;
        }

        public void move(Rectangle bounds) {
            Random temp = new Random();
            int rand = temp.nextInt(8);
            switch (rand) {
                case 1:
                    move_0();
                    break;
                case 2:
                    move_135();
                    break;
                case 3:
                    move_180();
                    break;
                case 4:
                    move_225();
                    break;
                case 5:
                    move_270();
                    break;
                case 6:
                    move_315();
                    break;
                case 7:
                    move_45();
                    break;
                case 8:
                    move_90();
                    break;
            }

            if (x < bounds.x) {
                x = bounds.x;
            } else if (x + size > bounds.x + bounds.width) {
                x = bounds.x + bounds.width - size;
            }
            if (y < bounds.y) {
                y = bounds.y;
            } else if (y + size > bounds.y + bounds.height) {
                y = bounds.y + bounds.height - size;
            }
        }

    }
}

See How to Use Swing Timers for more details

If you're hell bent on using a SwingWorker, then you should take the time to read through Worker Threads and SwingWorker

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366