2013-02-25 149 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