2013-02-25 58 views
1

我正在尝试创建一个Java小程序,它在小程序窗口内弹出几个球,每个小球都有自己的线程。用下面的代码画出所有球,但只有第一个移动。我究竟做错了什么?多线程Java小程序弹跳球

import java.applet.Applet; 
import java.awt.Color; 
import java.awt.Graphics; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 

import static java.awt.Color.*; 

public class BouncingBalls extends Applet implements Runnable { 

    List<Ball> ballList = new ArrayList(); // holds Ball objects 

    Color[] colors = new Color[]{BLACK, GRAY, WHITE, PINK, RED, ORANGE, YELLOW, 
      GREEN, BLUE, CYAN}; // array holding available colors 

    static int width, height; // variables for applet dimensions 

    int ballCount; // number of balls to be created, set by html parameter 

    Random random = new Random(); // random number generator 


    public void init() { 

     // get window dimensions 
     width = getSize().width; 
     height = getSize().height; 

     //get number of balls from html 
     String ballCountString = this.getParameter("ballCount"); 

     try { 
      ballCount = Integer.parseInt(ballCountString); 
     } catch (NumberFormatException e) { 
      ballCount = 10; // set to 10 by default 
     } 

     for (int i = 0; i < ballCount; i++) { 

      // randomly assign ballDiameter between 1 and 20 
      int ballDiameter = random.nextInt(20) + 1; 

      // create and add balls to ballList 
      ballList.add(new Ball(
        random.nextInt(width - ballDiameter), // set x coordinate 
        random.nextInt(height - ballDiameter), // set y coordinate 
        ballDiameter, // set ballDiameter 
        random.nextInt(ballDiameter) + 1, // deltaX <= ballDiameter 
        random.nextInt(ballDiameter) + 1, // deltaY <= ballDiameter 
        colors[i % 10] // use remainder to choose colors[] element 
        ) 
      ); 

     } // end for 

    } // end init 


    public void start() { 

     for (Ball ball: ballList) { 

      Thread t; 
      t = new Thread(this); 
      t.start(); 

     } // end for 

    } // end start 


    public void run() { 

     for (Ball ball : ballList) { 

      // infinite loop: ball moves until applet is closed 
      while (true) { 

       ball.move(); 

       repaint(); // call paint method to draw circle in new location 

       // set ball repaint delay using Thread sleep method 
       try { 
        Thread.sleep(20); // wait 20 msec before continuing 
       } catch (InterruptedException e) { 
        return; 
       } 

      } // end while 

     } // end for 

    } // end run 

    public void paint(Graphics g) { 

     super.paint(g); 

     for (Ball ball : ballList) { 

      // set current color 
      g.setColor(ball.ballColor); 

      // draw filled oval using current x and y coordinates and diameter 
      g.fillOval(ball.x, ball.y, ball.diameter, ball.diameter); 

     } // end for 

    } // end paint 
} 

class Ball { 

    int x, y, // coordinates of upper-left corner of circle 
     diameter, // circle diameter 
     deltaX, deltaY; // number of pixels ball moves each time it's repainted 
    Color ballColor; 


    public Ball(int x, int y, int diameter, int deltaX, int deltaY, 
       Color ballColor) { 

     this.x = x; 
     this.y = y; 
     this.diameter = diameter; 
     this.deltaX = deltaX; 
     this.deltaY = deltaY; 
     this.ballColor = ballColor; 

    } // end Ball constructor 


    public void move() { 

     // update x and y coordinates using delta values 
     x += deltaX; 
     y += deltaY; 

     // reverse x direction when ball reaches boundary 
     if (x >= BouncingBalls.width - diameter || x <= 0){ 
      deltaX = -deltaX; 
     } // end if 

     // reverse y direction when ball reaches boundary 
     if (y >= BouncingBalls.height - diameter || y <= 0) { 
      deltaY = -deltaY; 
     } // end if 

    } // end move 

} // end BouncingBalls 

回答

6

您的while(true)应该在for循环之外。它坐在从迭代器返回的第一个球上。

这就是说,你可能想要检查一个球的每个线程的逻辑。它实际上似乎创建了N个线程(N代表球的数量),其中每个线程将移动所有球而不是一个球。

编辑以解决我的第二点:

比方说,你有10个球。你开始10个线程,每个线程遍历所有的球。

例如:

Thread 1: 
public void run(){ 
    for(Ball b : ballList){ 
     b.move(); 
     b.repaint(); 
    } 
} 

Thread 2: 
public void run(){ 
    for(Ball b : ballList){ 
     b.move(); 
     b.repaint(); 
    } 
} 

Thread N: 
public void run(){ 
    for(Ball b : ballList){ 
     b.move(); 
     b.repaint(); 
    } 
} 

这是因为你有一个可运行的,同样的this实例创建线程完成在每个迭代球。

public void start() { 

     for (Ball ball: ballList) { 

      Thread t; 
      t = new Thread(this); 
      t.start(); 

     } // end for 

    } // end start 

所以我会想,如果每个球应该移动1个单位每20毫秒。你应该看到每20毫秒移动N * 1个单位,在这种情况下每20毫秒移动10个毫秒。

编辑 - 关于建议。

而不是设置this作为可运行的,则应该从this类中删除Runnable的执行情况,并创建一个新的Runnable,将采取单球作为参数。

private static class MovingRunnable implements Runnable{ 
    private final Ball b; 
    private MovingRunnable(Ball b){this.b=b;} 
    public void run(){ 
     for(;;){ 
     b.move(); 
     b.repaint(); 
     Thread.sleep(20); 
     } 
    } 
} 

在您启动方法

public void start() { 
     for (Ball ball: ballList) { 
      Thread t; 
      t = new Thread(new MovingRunnable(ball)); 
      t.start(); 
     } // end for 

    } // end start 

后来所以在这里每个球都有它自己的线程有它自己的Runnable。每个球现在只会每20毫秒每个线程调用一次move

但它仍然不是完美的,因为repaint只应该由UI线程调用,让它由每个线程调用可能会导致不同的问题(尽管你可能没有注意到任何,只是值得说)。

+0

非常感谢,约翰。交换时间并固定它。虽然我不认为我遵循每个线程如何移动所有的球。 – ChrisDevo 2013-02-25 17:33:03

+0

太好了。让我在编辑中解释。 – 2013-02-25 17:34:07

+0

有关如何让Ball类扩展Thread(或实现Runnable)的任何建议,以便每个都以自己的线程运行? – ChrisDevo 2013-02-26 16:22:39