-1

我正在尝试使用一个线程来控制弹跳球。我可以添加球、删除球、暂停运动,但是当我尝试恢复弹跳球的运动时,notify()/notifyAll() 不起作用。我只想要一个线程来控制正在添加到列表中的球的运动。我会很感激一个简单的解释,因为我是一个完全的新手。这是代码:

 ************************************************
 public class BounceBallApp extends JApplet {
 public BounceBallApp() {
 add(new BallControl());
 }

 public static void main(String[] args) {
     BounceBallApp applet = new BounceBallApp();
     JFrame frame = new JFrame();
     frame.add(applet); //new line added as per the reference
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setTitle("Assig_1_Base");
     frame.add(applet, BorderLayout.CENTER);
     frame.setSize(400, 320);
     frame.setVisible(true);
   }
}
***************************************************************
 public class BallControl extends JPanel {

private BallPanel ballPanel = new BallPanel();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JScrollBar jsbDelay = new JScrollBar();
private JButton jbtAddBall = new JButton("+1");
private JButton jbtDeleteBall = new JButton("-1");

    public BallControl() {

    JPanel panel = new JPanel();
    panel.add(jbtSuspend);
    panel.add(jbtResume);
    panel.add(jbtAddBall);
    panel.add(jbtDeleteBall);
    ballPanel.add();


   ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
   jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
   ballPanel.setDelay(jsbDelay.getMaximum());
   setLayout(new BorderLayout());
   add(jsbDelay, BorderLayout.NORTH);
   add(ballPanel, BorderLayout.CENTER);
   add(panel, BorderLayout.SOUTH);

  // Register listeners
   jbtSuspend.addActionListener(new ActionListener() {
    @Override
    public synchronized void actionPerformed(ActionEvent e) {
        ballPanel.suspend();
     }
    });
  jbtResume.addActionListener(new ActionListener() {
   @Override
   public synchronized void actionPerformed(ActionEvent e) {
       ballPanel.resume();
    }
   });

  jbtAddBall.addActionListener(new ActionListener() {
   @Override
    public synchronized void actionPerformed(ActionEvent e) {
      ballPanel.add();
    }
  });
  jbtDeleteBall.addActionListener(new ActionListener() {
    @Override
    public synchronized void actionPerformed(ActionEvent e) {
      ballPanel.delete();
    }
  });
 jsbDelay.addAdjustmentListener(new AdjustmentListener() {
   @Override
   public void adjustmentValueChanged(AdjustmentEvent e) {
    ballPanel.setDelay(jsbDelay.getMaximum() - e.getValue());
  }
 });
}

}


public class BallPanel extends JPanel {

  private int delay = 10;   
  private List<Ball> ballsArray = Collections.synchronizedList(new ArrayList());
  //protected List<Ball> ballsArray = new ArrayList<>();
  private int radius = 5; 
  boolean threadSuspended = false;

  public BallPanel() {
    start();
  }

  protected void start(){
   Thread t;
     t = new Thread(){
        @Override
         public void run(){
            System.out.println("*************");
             while (true){
                repaint();
                try {
                    Thread.sleep(delay);
                       synchronized(this){
                        if (threadSuspended==true) {wait();}
                       }
                } catch (InterruptedException e){
                    e.getMessage();
                }
            }
        }
   };

    t.start();
 }

 @Override
  protected void paintComponent(Graphics g) {
  super.paintComponent(g);

    for (Ball ball : ballsArray){

        g.setColor(ball.color);

                if (ball.x < 0 || ball.x > getWidth()) 
                  ball.dx *= -1;
        if (ball.y < 0 || ball.y > getHeight()) 
          ball.dy *= -1;

                ball.x += ball.dx;
        ball.y += ball.dy;
        g.fillOval(ball.x - radius, ball.y - radius, radius * 2, radius * 2);
    }   
}

public synchronized void suspend() {
  threadSuspended = true;
}

public synchronized void resume() {
  threadSuspended = false;
  notify();
}

public void setDelay(int delay) {
  this.delay = delay;
}
public void add(){
  if (threadSuspended==false)  ballsArray.add(new Ball());
}

public void delete(){
  if (ballsArray.size() > 0 && threadSuspended==false)
      ballsArray.remove(ballsArray.size() - 1); // Remove the last ball

 }
}

**************************************************
public class Ball  {

 int x = 0; 
 int y = 0;
 int dx = 2;
 int dy = 2;
 Color color = new Color(random(255),random(255),random(255));

     public static int random(int maxRange) {
     return (int) Math.round((Math.random() * maxRange));
  }

}

4

1 回答 1

1

你有一个“上下文”问题......

Thread在...的上下文中声明BallPane...

所以,当你打电话时wait(),你实际上是wait()在打电话t

Thread t;
t = new Thread(){
    @Override
    public void run(){
        /*...*/
        // this = t
        synchronized(this){
            // This is the same as saying
            // this.wait() or t.wait();
            if (threadSuspended==true) {wait();}
        }
    }
};

但是当你打电话时notify,你正在打电话BallPanenotify方法......

public synchronized void resume() {
    threadSuspended = false;
    // This is the same as this.notify or BallPane.this.notify()
    notify();
}

所以,t正在等待t's monitor lock 并且你调用BallPane'notify on it's monitor lock...意思是t永远不会被通知。

如果你使用共享锁会更好......

public class BallPanel extends JPanel {
    protected static final Object SUSPEND_LOCK = new Object();

    /*...*/

    Thread t;
    t = new Thread(){
        @Override
        public void run(){
            /*...*/
            synchronized(SUSPEND_LOCK ){
                if (threadSuspended==true) {SUSPEND_LOCK.wait();}
            }
        }
    };

    /*...*/

    public void resume() {
        synchronized(SUSPEND_LOCK){
            threadSuspended = false;
            SUSPEND_LOCK.notify();
        }
    }
于 2013-09-15T03:13:59.343 回答