多线程 Java 小程序弹跳球
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15072520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Multithreaded Java applet bouncing balls
提问by ChrisDevo
I'm trying to create a Java applet that bounces several balls within an applet window, each with its own thread. With the code below all the balls are drawn, but only the first one moves. What am I doing wrong?
我正在尝试创建一个 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
回答by John Vint
Your while(true)
should be outside the for loop. It sits on the first ball returned from the iterator.
您while(true)
应该在 for 循环之外。它位于从迭代器返回的第一个球上。
That being said, you may want to go over your logic for one ball-per-thread. It actually appears to create N number of threads (N being the number of balls) in which each thread will move all the balls instead of just one.
话虽如此,您可能想要检查每个线程一个球的逻辑。它实际上似乎创建了 N 个线程(N 是球的数量),其中每个线程将移动所有球而不是一个。
Edit to address my second point:
编辑以解决我的第二点:
Let's say you have 10 balls. You start 10 threads, and each thread iterates over all balls.
假设你有 10 个球。您启动 10 个线程,每个线程遍历所有球。
For instance:
例如:
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 is done because you create threads with the same this
instance of a runnable that iterates over each ball.
这样做是因为您使用this
迭代每个球的可运行对象的相同实例创建线程。
public void start() {
for (Ball ball: ballList) {
Thread t;
t = new Thread(this);
t.start();
} // end for
} // end start
So I would think, if each ball is supposed to move 1 unit every 20 milliseconds. You should end up seeing every 20 milliseconds moving N*1 units in this case 10 every 20 milliseconds.
所以我想,如果每个球应该每 20 毫秒移动 1 个单位。您最终应该看到每 20 毫秒移动 N*1 个单位,在这种情况下,每 20 毫秒移动 10 个单位。
Edit - With regards to a suggestion.
编辑 - 关于建议。
Instead of setting this
as the runnable you should remove the implementation of Runnable from the this
class and create a new Runnable that will take a single Ball as a parameter.
this
您应该从this
类中删除 Runnable 的实现,而不是设置为 runnable,并创建一个将单个 Ball 作为参数的新 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);
}
}
}
Later in your start method
稍后在您的 start 方法中
public void start() {
for (Ball ball: ballList) {
Thread t;
t = new Thread(new MovingRunnable(ball));
t.start();
} // end for
} // end start
So here each Ball has it's own thread with it's own Runnable. Each ball will now only invoke move
once per thread every 20 milliseconds.
所以这里每个 Ball 都有自己的线程和自己的 Runnable。每个球现在move
每个线程每 20 毫秒只调用一次。
But it still isn't perfect because repaint
should only be invoked by the UI thread, having it invoked by each thread can cause different issues (though you may not notice any, it's just worth saying).
但它仍然不完美,因为repaint
应该只由 UI 线程调用,让每个线程调用它可能会导致不同的问题(尽管您可能没有注意到任何问题,但值得一提)。