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

Java多线程同步需求:等待线程执行完成后修改类属性

解决Java线程等待任务完成后执行后续操作的问题

嘿,我明白你的问题了——你现在需要让zoom和zoom1完成所有放大、图像像素复制这些操作后,再去调用activarBoolTrue()方法,而且之前没接触过Java线程对吧?我一步步给你讲怎么实现,保证清晰易懂。

首先得说你当前代码的小问题:现在所有逻辑都跑在Swing的鼠标点击事件线程(也就是EDT,事件调度线程)里,那两个400x400的像素循环是挺耗时的,会直接卡住你的界面,而且你想等这些操作做完再执行后续方法,所以必须把这些耗时活儿放到后台线程里,然后等它们全部完成后再触发那两个方法。

具体实现步骤

我给你两种常用的方案,你选哪个都行:


方案一:用CountDownLatch等待多个任务完成

CountDownLatch是Java里专门用来等多个线程完成的工具类,初始化时设好要等的任务数量,每个任务做完就“倒计时”一次,等倒计时到0,就可以执行后续操作了。

重构后的鼠标点击事件代码如下:

public void mouseClicked(MouseEvent arg0) {
    // 初始化计数器,要等2个任务(zoom和zoom1)
    CountDownLatch taskLatch = new CountDownLatch(2);

    // 封装zoom的处理任务
    Runnable zoomProcessingTask = () -> {
        try {
            if (auxContadorZoom < 3 && auxContadorZoom >= 0) {
                zoom.aumentar(100);
                // 耗时的像素复制操作
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        image2.setRGB(i, j, zoom.enviar().getRGB(i, j));
                    }
                }
                // 注意:Swing组件的更新必须回到EDT线程!
                SwingUtilities.invokeLater(() -> {
                    label2.setIcon(new ImageIcon(zoom.enviar()));
                    label2.removeAll();
                    label2.add(zoom);
                    label2.repaint();
                    auxContadorZoom++;
                });
            }
        } finally {
            // 不管任务成功失败,都让计数器减1
            taskLatch.countDown();
        }
    };

    // 封装zoom1的处理任务
    Runnable zoom1ProcessingTask = () -> {
        try {
            if (auxContadorZoom1 < 3 && auxContadorZoom1 >= 0) {
                zoom1.aumentar(100);
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        image.setRGB(i, j, zoom1.enviar().getRGB(i, j));
                    }
                }
                SwingUtilities.invokeLater(() -> {
                    label.setIcon(new ImageIcon(zoom1.enviar()));
                    label.removeAll();
                    label.add(zoom1);
                    label.repaint();
                    auxContadorZoom1++;
                });
            }
        } finally {
            taskLatch.countDown();
        }
    };

    // 启动两个后台线程执行任务
    new Thread(zoomProcessingTask).start();
    new Thread(zoom1ProcessingTask).start();

    // 启动一个专门等待的线程,等两个任务都做完再执行后续方法
    new Thread(() -> {
        try {
            // 阻塞等待,直到计数器变为0(两个任务都完成)
            taskLatch.await();
            // 现在可以安全执行你要等的方法了
            zoom1.activarBoolTrue();
            zoom.activarBoolTrue();
            // 如果这两个方法涉及UI更新,记得也要用SwingUtilities.invokeLater包裹
            // SwingUtilities.invokeLater(() -> {
            //     zoom1.activarBoolTrue();
            //     zoom.activarBoolTrue();
            // });
        } catch (InterruptedException e) {
            // 处理线程中断异常,恢复中断状态是好习惯
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }).start();
}

方案二:用ExecutorService和Future管理线程

如果你想更规范地管理线程资源,可以用线程池ExecutorService,通过Future对象来等待任务完成:

public void mouseClicked(MouseEvent arg0) {
    // 创建一个固定大小的线程池,容纳2个线程
    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    // 封装zoom任务(和方案一的任务内容一样)
    Runnable zoomTask = () -> {
        try {
            if (auxContadorZoom < 3 && auxContadorZoom >= 0) {
                zoom.aumentar(100);
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        image2.setRGB(i, j, zoom.enviar().getRGB(i, j));
                    }
                }
                SwingUtilities.invokeLater(() -> {
                    label2.setIcon(new ImageIcon(zoom.enviar()));
                    label2.removeAll();
                    label2.add(zoom);
                    label2.repaint();
                    auxContadorZoom++;
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    // 封装zoom1任务
    Runnable zoom1Task = () -> {
        try {
            if (auxContadorZoom1 < 3 && auxContadorZoom1 >= 0) {
                zoom1.aumentar(100);
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        image.setRGB(i, j, zoom1.enviar().getRGB(i, j));
                    }
                }
                SwingUtilities.invokeLater(() -> {
                    label.setIcon(new ImageIcon(zoom1.enviar()));
                    label.removeAll();
                    label.add(zoom1);
                    label.repaint();
                    auxContadorZoom1++;
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    // 提交任务到线程池,获取Future对象用来等待任务完成
    Future<?> zoomFuture = threadPool.submit(zoomTask);
    Future<?> zoom1Future = threadPool.submit(zoom1Task);

    // 等待任务完成并执行后续方法
    new Thread(() -> {
        try {
            // 等待两个任务都执行完毕
            zoomFuture.get();
            zoom1Future.get();
            // 执行目标方法
            zoom1.activarBoolTrue();
            zoom.activarBoolTrue();
            // 关闭线程池,释放资源
            threadPool.shutdown();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            // 出现异常时强制关闭线程池
            threadPool.shutdownNow();
        }
    }).start();
}

关键注意点

  1. 别阻塞EDT线程:Swing的所有UI操作都在EDT里执行,耗时任务必须放到后台线程,否则界面会卡死。
  2. UI操作必须回EDT:所有修改Swing组件(比如label的图标、repaint)的代码,一定要用SwingUtilities.invokeLater()包裹,因为Swing不是线程安全的。
  3. 异常处理:不管用哪种方案,都要处理线程中断或执行异常,避免程序崩溃。

内容的提问来源于stack exchange,提问作者DARYZ Fifa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:56:47