【问题标题】:How to use Java ThreadPool in a bouncing balls program?如何在弹跳球程序中使用 Java ThreadPool?
【发布时间】:2014-01-02 22:41:13
【问题描述】:

我有这个程序,它是一个带有 20 个弹跳球(线程)的窗口,它们具有随机生成的大小、速度、方向和位置。它们还具有随机生成的最大反弹次数,可以反弹到墙壁上。当超过该数字时,Thread 将停止工作。 我的问题是如何将ThreadPool 合并到程序中,以便当Thread 停止工作时,会启动一个新的而不是它?

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;
    }
}

【问题讨论】:

  • 不确定为什么要使用线程来执行此操作。请记住,Swing 不是线程安全的,因此更新需要与 EDT 同步,并且绘制和更新应该同步,这样球在绘制时不会更新。看一下java.util.concurrncy包中的ExecutorService
  • 我同意@MadProgrammer。每个球使用一根线可能是不必要的,而且重量很大。我建议更改您的实现,以便“球”不会扩展线程,而是只有一个“游戏线程”更新所有球并在每次迭代时重新绘制每个球。这是这类游戏的常见做法。在任何情况下,您都会遇到问题,因为您的代码不是线程安全的。在访问 Ball 成员变量(即thisBall)之前,您需要在 Swing 线程和 Ball 线程之间进行同步。
  • 引用hereKineticModel 使用单个javax.swing.Timer 来调整动画速度。
  • 您在之前的问题中遇到了不好的答案,请使用 Swing Timer 而不是 Thread

标签: java multithreading swing threadpool


【解决方案1】:

如果您的目标是让弹跳球动画线程在超过 maxBounces 后继续运行,您只需重置计数即可:

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
}

如果您想使用 ThreadPool 来使用它,那么在完成您的 Ball#run() 后,您必须安排新的 Ball 作业(可运行)并将其添加到 ThreadPool .所以,你必须这样做:

  1. 让您的Ball 实现Runnable(或Callable),而不是扩展Thread
  2. 创建执行器(线程池):

    ExecutorService executor = Executors.newFixedThreadPool(numOfThreads);
    
  3. 当您的 runnable 由于 maxBounces 条件而即将停止时,您需要将相同的 runnable 添加到池中:

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

注意: 正如其他人评论的那样,必须修复同步问题才能可靠地工作,我还建议阅读有关 ThreaPool'swhen they can be useful 的信息,因为我认为没有理由在你的情况下使用一个。

希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 2012-10-12
    • 1970-01-01
    • 2023-03-13
    • 2011-08-25
    • 2021-08-12
    相关资源
    最近更新 更多