【问题标题】:Multithreaded Java applet bouncing balls多线程 Java 小程序弹跳球
【发布时间】:2013-02-25 17:14:09
【问题描述】:

我正在尝试创建一个 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

【问题讨论】:

    标签: java multithreading applet


    【解决方案1】:

    您的 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
    

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

    编辑 - 关于建议。

    您应该从 this 类中删除 Runnable 的实现,并创建一个将单个 Ball 作为参数的新 Runnable,而不是将 this 设置为可运行对象。

    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
    

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

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

    【讨论】:

    • 谢谢一百万,约翰。交换 while 和 for 修复它。虽然我不认为我遵循每个线程如何移动所有球。
    • 关于如何让 Ball 类扩展 Thread(或实现 Runnable)以便每个都作为自己的线程运行的任何建议?
    • 再次感谢约翰。我对 Java 比较陌生(和一般的编码)。似乎我经常达到收益递减的地步,在解决一个问题数小时和数天后,我无法弄清楚回想起来似乎简单而明显的事情。
    • @ChrisDevo 是的,我明白,我们都去过那里。话虽这么说,多线程编程非常困难,所以至少要保持透视:)。
    猜你喜欢
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 2013-01-13
    • 1970-01-01
    • 2011-08-25
    • 2012-10-12
    相关资源
    最近更新 更多