You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线程运行时Java多线程AWT绘图程序无法关闭及操作求助

多线程AWT绘图程序无法关闭的问题修复

核心问题分析

  • GUI事件线程(EDT)被阻塞:MyCanvas的paint()方法里包含持续的while (!Marble.isDone())循环,配合Util.delay()的忙等逻辑,直接霸占了负责处理所有GUI事件(窗口关闭、按钮点击等)的EDT,导致用户操作完全无法响应。
  • 线程可见性问题:Marble类中的done静态变量未加volatile修饰,多线程环境下其他线程对done的修改无法被主线程或其他线程及时感知。
  • 不安全的绘图操作:直接在非EDT线程中使用Canvas的Graphics对象绘图,违反AWT/Swing线程安全规则,可能引发绘图异常或线程阻塞。
  • 低效的忙等延迟:Util.delay()用忙循环实现延迟,持续占用CPU资源,加剧线程阻塞问题。

修复步骤

  1. 释放EDT,移除paint方法中的阻塞循环,仅保留线程初始化逻辑。
  2. 给Marble的done变量添加volatile修饰符,确保多线程可见性。
  3. 替换Util.delay()为Thread.sleep(),避免CPU空转并正确处理中断异常。
  4. 使用双缓冲绘图:创建BufferedImage作为绘图缓冲区,所有Marble线程先绘制到缓冲区,再由EDT统一绘制到Canvas,保证线程安全。
  5. 优化线程初始化逻辑,避免paint方法重复调用时创建多组线程。

修改后的完整代码

// MultiThreadedSnakes.java
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class MultiThreadedSnakes {
    public static void main(String args[]) {
        SwingUtilities.invokeLater(() -> {
            Snakes tester = new Snakes();
            tester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        });
    }
}

class Snakes extends JFrame {
    private Container contentPane;
    private MyCanvas canvas;
    private JButton stopButton;

    public Snakes() {
        super("MultiThreadedSnakes");

        stopButton = new JButton("Stop Execution and Close Window");
        stopButton.setFont(new Font("Arial", Font.BOLD, 24));
        stopButton.addActionListener(evt -> {
            Marble.terminate();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.exit(0);
        });

        contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.add(stopButton, BorderLayout.PAGE_END);
        canvas = new MyCanvas();
        contentPane.add(canvas, BorderLayout.CENTER);
        setSize(1320, 820);
        setVisible(true);
    }
}

class Marble implements Runnable {
    private Marble[] marbles;
    private int width, height;
    private int size;
    private int xCoord, yCoord;
    private int incX, incY;
    private Color clr;
    private BufferedImage buffer;
    private static volatile boolean done = false; // 添加volatile保证多线程可见性

    public Marble(int w, int h, int x, int y, int adjustX, int adjustY, BufferedImage buf, Color c, Marble[] m) {
        marbles = m;
        width = w;
        height = h;
        size = 30;
        xCoord = x;
        yCoord = y;
        incX = adjustX;
        incY = adjustY;
        clr = c;
        this.buffer = buf;
    }

    public static void terminate() {
        done = true;
    }

    public static boolean isDone() {
        return done;
    }

    public void moveMarble() {
        Random rnd = new Random();
        xCoord += incX;
        yCoord += incY;

        if (xCoord <= 0)
            incX = (rnd.nextInt(11) + 1);
        else if (xCoord >= width - size)
            incX = -(rnd.nextInt(11) + 1);

        if (yCoord <= 0)
            incY = (rnd.nextInt(11) + 1);
        else if (yCoord >= height - size)
            incY = -(rnd.nextInt(11) + 1);
    }

    public void showMarble() {
        synchronized (marbles) {
            Graphics g = buffer.getGraphics();
            g.setColor(clr);
            g.fillOval(xCoord, yCoord, size, size);
            g.dispose();
        }
    }

    public void run() {
        while (!done) {
            showMarble();
            try {
                Thread.sleep(10); // 替换忙等为标准线程休眠
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
            moveMarble();
        }
    }
}

class MyCanvas extends Canvas {
    private int maxX, maxY;
    private int numMarbles;
    private Marble[] marbles;
    private Thread[] threads;
    private BufferedImage buffer;

    public MyCanvas() {
        setSize(1320, 760);
        maxX = 1300;
        maxY = 700;
        numMarbles = 8;
        marbles = new Marble[numMarbles];
        threads = new Thread[numMarbles];
        // 初始化双缓冲图像
        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
    }

    public void paint(Graphics g) {
        // 仅初始化一次线程,避免重复创建
        if (threads[0] == null) {
            Graphics bufG = buffer.getGraphics();
            bufG.setColor(Color.white);
            bufG.fillRect(0, 0, getWidth(), getHeight());
            bufG.dispose();

            marbles[0] = new Marble(maxX, maxY, 50, 50, 5, 5, buffer, Color.red, marbles);
            marbles[1] = new Marble(maxX, maxY, 100, 100, 5, 5, buffer, Color.green, marbles);
            marbles[2] = new Marble(maxX, maxY, 150, 150, 5, 5, buffer, Color.blue, marbles);
            marbles[3] = new Marble(maxX, maxY, 200, 200, 5, 5, buffer, Color.orange, marbles);
            marbles[4] = new Marble(maxX, maxY, 250, 250, 5, 5, buffer, Color.gray, marbles);
            marbles[5] = new Marble(maxX, maxY, 300, 300, 5, 5, buffer, Color.pink, marbles);
            marbles[6] = new Marble(maxX, maxY, 350, 350, 5, 5, buffer, Color.yellow, marbles);
            marbles[7] = new Marble(maxX, maxY, 400, 400, 5, 5, buffer, Color.black, marbles);

            for (int t = 0; t < numMarbles; t++) {
                threads[t] = new Thread(marbles[t]);
                threads[t].start();
            }
        }

        // 将缓冲区绘制到Canvas
        g.drawImage(buffer, 0, 0, this);
        // 主动触发重绘更新画面
        if (!Marble.isDone()) {
            repaint();
        }
    }

    @Override
    public void update(Graphics g) {
        // 覆盖update方法,避免默认清屏操作,减少画面闪烁
        paint(g);
    }
}

class Util {
    public static int random(int min, int max) {
        int range = max - min + 1;
        return (int) (Math.random() * range + min);
    }
}

关键修改说明

  • 释放EDT:移除paint方法中的阻塞循环,改用repaint()主动更新画面,让EDT能正常处理GUI事件。
  • volatile修饰done:确保所有线程能及时感知done变量的变化,避免线程无限循环。
  • Thread.sleep替代忙等:降低CPU占用,同时正确处理中断,让线程能响应终止信号。
  • 双缓冲绘图:统一绘图流程,符合AWT线程安全规则,减少画面闪烁。
  • 线程初始化判断:避免paint方法重复调用时创建多组线程,防止资源浪费。

内容的提问来源于stack exchange,提问作者John Schram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:05:18