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

Processing中mouseClicked()函数引发的线程异常问题求助

问题分析与修复方案

核心问题拆解

你的代码存在几个关键逻辑冲突,导致重复点击时线程异常:

  1. 同步方法阻塞引发逻辑混乱:update、setScreenIndex、waitForThread全是synchronized方法,线程加载时重复点击触发的setScreenIndex会阻塞等待锁,同时update方法也会被卡住,无法正常轮询线程状态,最终导致线程看似存活但实际无响应。
  2. 线程运行时屏幕引用被篡改:重复点击会调用setScreenIndex修改screenIndex和currentScreen,正在运行的SetupScreenThread持有的屏幕引用可能被替换,导致初始化逻辑执行到一半失效。
  3. 线程状态轮询不可靠:仅靠isAlive()判断线程是否完成并不准确——线程可能因异常悄悄终止,但isAlive()返回false的时机无法保证;或是线程陷入死循环,导致isAlive()一直为true。

具体修复步骤

1. 新增加载状态拦截,直接拒绝重复请求

在Main类中添加volatile加载标记,拦截重复点击触发的无效请求:

final class Main
{
  private volatile boolean isLoading = false; // 新增加载状态标记

  public void update() // 移除全局同步,仅同步关键代码块
  {
    try
    {
      Thread threadToCheck = null;
      int targetIndex = -1;
      // 仅同步变量读取操作
      synchronized(this) {
        threadToCheck = threadToWaitFor;
        targetIndex = nextScreenIndex;
      }

      if (threadToCheck != null)
      {
        if (!threadToCheck.isAlive())
        {
          synchronized(this) {
            threadToWaitFor = null;
            isLoading = false; // 重置加载状态
          }
          setScreenIndex(targetIndex);
        }
      }
      // 同步屏幕更新操作
      synchronized(this) {
        currentScreen.update();
      }
    }
    catch (Exception e)
    {
      logger.println("ERROR: Main Update: " + e.getMessage());
      logger.println("Stack Trace: ");
      e.printStackTrace(logger);
      logger.flush();
      exit();
    }
  }

  public synchronized void setScreenIndex(int screenIndex)
  {
    if (isLoading) { // 拦截正在加载时的重复请求
      return;
    }
    this.screenIndex = screenIndex;
    currentScreen = screens.get(this.screenIndex);
    if (!currentScreen.getSetup())
    {
      isLoading = true; // 标记进入加载状态
      // 传入Main实例用于线程完成后的回调
      SetupScreenThread thread = new SetupScreenThread(currentScreen, this);
      thread.start();
      waitForThread(thread, screenIndex);
    }
  }

  public synchronized void waitForThread(Thread thread, int newScreenIndex)
  {
    this.threadToWaitFor = thread;
    this.nextScreenIndex = newScreenIndex;
    this.currentScreen = loadScreen;
  }

  // 新增回调方法,线程完成后调用以重置状态
  public synchronized void onScreenSetupComplete() {
    isLoading = false;
  }
}

2. 修改线程类,确保初始化逻辑独立且异常可控

让线程持有独立的屏幕引用,避免外部篡改,同时捕获所有异常并通知主类:

class SetupScreenThread extends Thread {
  private final Screen targetScreen;
  private final Main mainInstance;

  public SetupScreenThread(Screen targetScreen, Main mainInstance) {
    this.targetScreen = targetScreen;
    this.mainInstance = mainInstance;
  }

  @Override
  public void run() {
    try {
      targetScreen.setup(); // 执行屏幕初始化
      targetScreen.setSetup(true); // 标记初始化完成
    } catch (Exception e) {
      // 记录初始化异常,避免线程悄悄终止
      mainInstance.logger.println("ERROR: Screen Setup: " + e.getMessage());
      e.printStackTrace(mainInstance.logger);
      mainInstance.logger.flush();
    } finally {
      mainInstance.onScreenSetupComplete(); // 无论成功失败都通知主类更新状态
    }
  }
}

3. 可选优化:替换轮询为主动通知机制

如果轮询isAlive()仍有问题,可以用CountDownLatch替代轮询,让线程完成后主动唤醒主逻辑:

// 在Main类中替换threadToWaitFor为CountDownLatch
private volatile CountDownLatch setupLatch = null;

// 修改setScreenIndex中的线程启动逻辑
SetupScreenThread thread = new SetupScreenThread(targetScreen, this);
setupLatch = new CountDownLatch(1);
thread.start();
waitForThread(screenIndex);

// 修改update中的检查逻辑
if (setupLatch != null) {
  try {
    // 非阻塞等待,避免卡住UI更新
    setupLatch.await(100, TimeUnit.MILLISECONDS);
    if (setupLatch.getCount() == 0) {
      setupLatch = null;
      isLoading = false;
      setScreenIndex(targetIndex);
    }
  } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
  }
}

// 在SetupScreenThread的finally中调用
setupLatch.countDown();

关键注意点

  • 缩小同步范围:避免全局同步方法,只同步必要的变量读写和状态更新,保证UI更新流畅性。
  • 线程引用独立:线程操作的屏幕对象不要被外部修改,确保初始化逻辑完整执行。
  • 强制捕获异常:线程中的未捕获异常会导致线程静默终止,必须捕获并记录,避免状态不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 08:05:12