0

I have this program which is a window with 20 bouncing balls (threads) which have randomly generated size, speed, direction and location. They also have a randomly generated maximum number of bounces against the walls they can take. When that number is exceeded the Thread stops its work. My question would be how could I incorporate a ThreadPool into the program so that when one Thread stops its work a new one is started instead of it?

public class BouncingBalls extends JPanel{

    private ArrayList<Ball> balls = new ArrayList<Ball>();
    private static final long serialVersionUID = 1L;
    private static final int box_width = 1000;
    private static final int box_height = 800;

    public BouncingBalls() {
        this.setPreferredSize(new Dimension(box_width, box_height));
        createBalls();
        gameStart();
    }

   public void createBalls(){
        for(int i=0;i<20;i++){
            Ball ball = new Ball(this);
            balls.add(ball);
        }
    }

   public void gameStart() {
       Thread gameThread = new Thread() {
           public void run() {
               for(Ball b : balls){
                   b.start();
               }
            }
      };
      gameThread.start();
   }

   @Override
    public void paintComponent(Graphics g)  {
       super.paintComponent(g);
       Graphics2D g2d = (Graphics2D) g;
       for(Ball b : balls){
            b.draw(g2d);
       }
   }

   public static void main(String[] args) {
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            JFrame frame = new JFrame("Bouncing Balls");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(new BouncingBalls());
            frame.pack();
            frame.setVisible(true);
         }
      });
   }
}   

public class Ball extends Thread{
    private Ellipse2D.Double thisBall;
    private int posX;
    private int posY;
    private int radius;
    private int maxBounces;
    private int bounces = 0;
    private int deltaX, deltaY;
    private BouncingBalls mainWindow;

    public Ball(BouncingBalls mainWindow){
        this.posX = 0 + (int)(Math.random() * ((975 - 0) + 1));
        this.posY = 0 + (int)(Math.random() * ((775 - 0) + 1));
        this.radius = 6 + (int)(Math.random() * ((20 - 6) + 1));
        this.deltaX = -10 + (int)(Math.random() * 21);
        this.deltaY = -10 + (int)(Math.random() * 21);
        this.maxBounces = 10 + (int)(Math.random() * ((25 - 10) + 1));
        this.mainWindow = mainWindow;
        thisBall = new Ellipse2D.Double(posX, posY, radius, radius);
    }

    public void draw(Graphics2D g2d){
            g2d.setColor(Color.RED);
            g2d.fill(thisBall);
            g2d.setColor(Color.BLACK);
            mainWindow.repaint();
    }

    public void run(){
        while(true){
            int oldx = (int) thisBall.getX();
            int oldy = (int) thisBall.getY();
            int newx = oldx + deltaX;
            if (newx + radius > 995 || newx <= 5){
               deltaX = -deltaX;
               bounces++;
            }   
            int newy = oldy+ deltaY;
            if (newy + radius > 795 || newy <= 5){ 
               deltaY = -deltaY; 
               bounces++;
            }
            thisBall.setFrame(newx, newy, radius, radius);
            this.posX = newx;
            this.posY = newy;
            mainWindow.repaint();
            if(bounces>=maxBounces){
                this.stop();
            }
            try {
                   Thread.sleep(30);
            }
            catch (InterruptedException e){  
                System.out.println("Woke up prematurely");
            }
        }
    }

    public int getPosX() {
        return posX;
    }

    public void setPosX(int posX) {
        this.posX = posX;
    }

    public int getPosY() {
        return posY;
    }

    public void setPosY(int posY) {
        this.posY = posY;
    }
}
kiruwka
  • 9,250
  • 4
  • 30
  • 41
Helena
  • 921
  • 1
  • 15
  • 24
  • 1
    Not sure why you want to use threads to do this. Remember, Swing is not thread safe, so updates needs to be synchronised with the EDT and painting and updating should be synchronized so that the ball is not updated while it is been painted. Take a look at the ExecutorService in the java.util.concurrncy package – MadProgrammer Jan 02 '14 at 22:52
  • 1
    I agree with @MadProgrammer. Using a thread per-ball is likely unnecessary and very heavy-weight. I would recommend changing your implementation so that "Ball" does not extend thread and instead have only one "game thread" that updates all of the balls and redraws each one per iteration. This is common practice for these types of games. In any case, you are going to be running into issues because your code is not thread safe. You need to synchronize between your Swing thread and your Ball threads before accessing the Ball member variables (i.e. `thisBall`). – torbinsky Jan 02 '14 at 23:08
  • 1
    The `KineticModel` cited [here](http://stackoverflow.com/a/11835051/230513) uses a single `javax.swing.Timer` to pace the animation. – trashgod Jan 03 '14 at 08:02
  • 1
    you have an issue with poor answers in youo previous questions, use Swing Timer instead of Thread – mKorbel Jan 03 '14 at 08:03

1 Answers1

0

If your goal is to have your bouncing ball animation threads continue running after maxBounces is exceeded you could simply do it by resetting the count :

if(bounces >= maxBounces) {
    // log the maxBounces exceeded message
    bounces = 0;
    // this.stop(); - don't do this, Thread#stop() is deprecated        
    // thread will continue until explicitly interrupted
}

If you want to use ThreadPool for the sake of using it then on completion of your Ball#run() you have to schedule new Ball job(runnable) and add it to the ThreadPool. So, you would have to do this :

  1. Make your Ball implement Runnable (or Callable) instead of extending Thread.
  2. Create executor (thread pool) :

    ExecutorService executor = Executors.newFixedThreadPool(numOfThreads);
    
  3. When your runnable is about to stop due to maxBounces condition you need to add same runnable to the pool:

    if (bounces >= maxBounces) {
        // reset the counter
        bounces = 0;
        executor.submit(this);
        return;
    }
    

Note : as others commented there are synchronization issues that have to be fixed to work reliably and I also recommend reading about ThreaPool's and when they can be useful as I don't think there is a reason to use one in your case.

Hope that helps.

Community
  • 1
  • 1
kiruwka
  • 9,250
  • 4
  • 30
  • 41