スレッドを使えばpaintImmediatelyを使わずに動かすことができます。動いている間もボタンが使えますから、複数の●を同時に動かせますし、green, blue ボタンも生きていますからこのようにいつでもランダムな円を描き加えることができます。

プログラムは決められた順番にしたがって作業をしていきます。この一連の作業の流れをスレッドといいます。
イベントを待ってボタンを押されたらactionPerformed()を実行するというのも一つのスレッドになっています。javaのイベントのスレッドをイベントディスパッチスレッド(EDT)と言います。
このイベントディスパッチスレッド内で時間のかかる仕事を入れてしまうと次のイベントが起こっても対応できません。そこで、時間のかかる仕事をEDTとは別のスレッドでやらせようという作戦です。
今回は円を描いてそれをゆっくり動かす仕事を別のスレッドにさせて、EDTはボタンのクリックを監視する作業にもどります。
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.*;
public class MovDisk2 extends JFrame implements ActionListener{
JButton rbtn;
JButton gbtn;
JButton bbtn;
MyPanel mypnl;
JPanel btnpnl;
//コンストラクタ
public MovDisk2() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("MovDisk");
mypnl = new MyPanel(400,400);
rbtn = new JButton("start"); //今回はこれだけ
gbtn = new JButton("green");
bbtn = new JButton("blue");
btnpnl = new JPanel();
btnpnl.setLayout(new GridLayout(1,3,0,0));
btnpnl.add(rbtn);
btnpnl.add(gbtn);
btnpnl.add(bbtn);
setLayout(new BorderLayout());
add(mypnl, BorderLayout.CENTER);
add(btnpnl,BorderLayout.SOUTH);
rbtn.addActionListener(this);
gbtn.addActionListener(this);
bbtn.addActionListener(this);
pack();
setVisible(true);
} //end of MovDisk2()コンストラクタ
//イベント処理
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == rbtn) {
Thread thread = new MakeDisk6Move(mypnl);
thread.start();
}
if (e.getSource() == gbtn) {
mypnl.drawRdm('g');
mypnl.repaint();
}
if (e.getSource() == bbtn) {
mypnl.drawRdm('b');
mypnl.repaint();
}
} //end of actionPerformed
public static void main(String[] args){
MovDisk2 myframe = new MovDisk2();
} // end of main
} // end of class MovDisk2
//クラス
class MyPanel extends JPanel{
Color c = new Color(0,0,0); //楕円の色(drawRdmで使用)
Color bc= new Color(255,255,191); //背景の色
BufferedImage buffimg;
Graphics bfg;
public MyPanel(int width, int height){
buffimg = new BufferedImage(
width,
height,
BufferedImage.TYPE_INT_RGB);
bfg = buffimg.createGraphics();
bfg.setColor(bc);
bfg.fillRect(0, 0, width, height);
setPreferredSize(new Dimension(width,height));
}
@Override
public void paintComponent(Graphics myg){
super.paintComponent(myg);
myg.drawImage(buffimg, 0, 0
,getSize().width, getSize().height,this);
//getSize().widthはMyPanelのインスタンスの幅
}
//円を動かすメソッド→下記クラスに移動
//ランダムに円を描くメソッド
public void drawRdm(char rgb) {
for(int i=0; 10>i; i++){
nextColor(rgb);
bfg.setColor(c);
int x = (int)(400*Math.random());
int y = (int)(400*Math.random());
int h = (int)(50*Math.random()+5);
bfg.fillOval(x-h/2,y-h/2,h,h);
}
}
//色を変化させるメソッド
public void nextColor(char rgb){
int r=0;
int g=0;
int b=0;
if (rgb=='r'){
r = (int)(r + 256*Math.random());
}
if (rgb=='g'){
g = (int)(g + 256*Math.random());
}
if (rgb=='b'){
b = (int)(b + 256*Math.random());
}
c = new Color(r,g,b);
}
} // end of class MyPanel
//クラス
class MakeDisk6Move extends Thread { //add
MyPanel pnl;
BufferedImage image;
Graphics bfg;
int x = 100;
int y ;
int d = 10;
int dx = 10;
int xmax,ymax;
//コンストラクタ
public MakeDisk6Move(MyPanel pn) {
pnl = pn;
image = pn.buffimg;
bfg = image.createGraphics();
xmax = image.getWidth();
ymax = image.getHeight();
y = (int)(ymax*Math.random());
}
@Override //add
public void run() { //add
bfg.setColor(Color.red);
while ( xmax > x ){
bfg.fillOval(x-d/2,y-d/2,d,d);
pnl.repaint();
x+=dx;
//100ms(0.1秒)停止
try {
Thread.sleep(100);
}
catch(InterruptedException ex) {
System.err.println(ex);
}
}//end of while
}//add end of run
}//end of class MakeDisk6Move
強調部分が主要な変更です。
一番大きな変更はmypnl.MakeDisk6Move()メソッドを独立したクラスにしたことです。そのために最後に持っていっています。
したがってイベント処理(actionPerformed)で、mypnl.moveDisk()の代わりにスレッドの作成(new MakeDisk6Move())と実行(start)をしています。
new MakeDisk6Move(mypnl);でmypnlを渡しています。MakeDisk6Moveではコンストラクタでそれをpnlでうけrepaint()を指令します。またそのbuffimgをimageという名前でうけてその大きさをを調べ、image.createGraphics()でGraphicsを取得して使います。
実行すると次の様になります。

別スレッドで実行されることを確認しなさい。