首先看两段类似的程序
单线程的

/** *//**
* @(#)Bounce.java
*
*
* @author livahu
* @version 1.00 2006/10/16
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;


public class Bounce ...{


public Bounce() ...{
}

public static void main(String[] args) ...{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}


class Ball ...{
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;
private double dy = 1;

public void move(Rectangle2D bounds) ...{
x += dx;
y += dy;

if (x < bounds.getMinX()) ...{
x = bounds.getMinX();
dx = -dx;
}

if (x + XSIZE >= bounds.getMaxX()) ...{
x = bounds.getMaxX() - XSIZE;
dx = -dx;
}

if (y < bounds.getMinY()) ...{
y = bounds.getMinY();
dy = -dy;
}

if (y + YSIZE >= bounds.getMaxY()) ...{
y = bounds.getMaxY() - YSIZE;
dy = -dy;
}
}

public Ellipse2D getShape() ...{
return new Ellipse2D.Double(x, y, XSIZE, YSIZE);
}
}


class BallPanel extends JPanel ...{
private ArrayList<Ball> balls = new ArrayList<Ball>();

public void add(Ball b) ...{
balls.add(b);
}

public void remove(Ball b) ...{
balls.remove(b);
}

public void paintComponent(Graphics g) ...{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;

for (Ball b : balls) ...{
g2.fill(b.getShape());
}
}
}


class BounceFrame extends JFrame ...{
private BallPanel panel;
public static final int DEFAULT_WIDTH = 450;
public static final int DEFAULT_HEIGHT = 350;
public static final int STEPS = 1000;
public static final int DELAY = 3;

public BounceFrame() ...{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
setTitle("Bounce");
panel = new BallPanel();
add(panel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start",

new ActionListener() ...{

public void actionPerformed(ActionEvent event) ...{
addBall();
}
});
addButton(buttonPanel, "Close",

new ActionListener() ...{

public void actionPerformed(ActionEvent event) ...{
System.exit(0);
}
});
add(buttonPanel, BorderLayout.SOUTH);
}

public void addButton(Container c, String title, ActionListener listener) ...{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}

public void addBall() ...{

try ...{
Ball ball = new Ball();
panel.add(ball);

for (int i = 1; i <= STEPS; i++) ...{
ball.move(panel.getBounds());
panel.paint(panel.getGraphics());
// panel.repaint(); 为什么不能使用repaint?使用repaint屏幕上没有任何球。repaint 和 paint有什么区别与关系
Thread.sleep(DELAY);
}
panel.remove(ball);

} catch(InterruptedException ex) ...{
}
}
}
多线程的

/** *//**
* @(#)Bounce.java
*
*
* @author livahu
* @version 1.00 2006/10/16
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;


public class BounceThread ...{


public BounceThread() ...{
}

public static void main(String[] args) ...{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}


class BallRunnable implements Runnable ...{
private Ball ball;
private BallPanel panel;
public static final int STEPS = 1000;
public static final int DELAY = 5;

public BallRunnable(Ball aBall, BallPanel panel) ...{
ball = aBall;
this.panel = panel;
}

public void run() ...{

try ...{

for (int i = 1; i <= STEPS; i++) ...{
ball.move(panel.getBounds());
panel.repaint();//这里又可以用repaint
Thread.sleep(DELAY);
}
panel.remove(ball);

} catch(InterruptedException ex) ...{
}
}
}


class Ball ...{
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;
private double dy = 1;

public void move(Rectangle2D bounds) ...{
x += dx;
y += dy;

if (x < bounds.getMinX()) ...{
x = bounds.getMinX();
dx = -dx;
}

if (x + XSIZE >= bounds.getMaxX()) ...{
x = bounds.getMaxX() - XSIZE;
dx = -dx;
}

if (y < bounds.getMinY()) ...{
y = bounds.getMinY();
dy = -dy;
}

if (y + YSIZE >= bounds.getMaxY()) ...{
y = bounds.getMaxY() - YSIZE;
dy = -dy;
}
}

public Ellipse2D getShape() ...{
return new Ellipse2D.Double(x, y, XSIZE, YSIZE);
}
}


class BallPanel extends JPanel ...{
private ArrayList<Ball> balls = new ArrayList<Ball>();

public void add(Ball b) ...{
balls.add(b);
}

public void remove(Ball b) ...{
balls.remove(b);
}

public void paintComponent(Graphics g) ...{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;

for (Ball b : balls) ...{
g2.fill(b.getShape());
}
}
}


class BounceFrame extends JFrame ...{
private BallPanel panel;
public static final int DEFAULT_WIDTH = 450;
public static final int DEFAULT_HEIGHT = 350;
public static final int STEPS = 1000;
public static final int DELAY = 3;

public BounceFrame() ...{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
setTitle("Bounce");
panel = new BallPanel();
add(panel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start",

new ActionListener() ...{

public void actionPerformed(ActionEvent event) ...{
addBall();
}
});
addButton(buttonPanel, "Close",

new ActionListener() ...{

public void actionPerformed(ActionEvent event) ...{
System.exit(0);
}
});
add(buttonPanel, BorderLayout.SOUTH);
}

public void addButton(Container c, String title, ActionListener listener) ...{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}

public void addBall() ...{
Ball b = new Ball();
panel.add(b);
Runnable r = new BallRunnable(b, panel);
Thread t = new Thread(r);
t.start();
}
}
从这两段程序,不难发现,本因调用repaint的单线程程序无法重绘图象,但在多线程里就没有一点问题。那repaint和paint在线程上有什么区别类?我想了很久,最后感谢zxyxc的帮助,让我知道了事件分配的一些细节。
是这样的:Java中事件的分配调度是由专门的线程完成的。当调用repaint方法时,实际上是发送一个重绘事件到事件队列,而后事件分配线程会调用paint方法来完成重绘。当在多线程时,一切正常,因为是调用的repaint方法的线程没有占据全部的CPU时间,所以事件分配线程可以执行,并调用paint完成重绘。
而在单线程情况下,由于主线程完全占据了CPU,事件分配线程无法执行,自然也就没办法去分配通过调用repaint方法插入的重绘事件。取代的做法就是直接调用paint(),而不是等待事件分配线程去调用。