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

Jena自定义PropertyFunction等待服务器响应时抛出IllegalMonitorStateException

解决你的Jena PropertyFunction中wait()异常与等待线程问题

首先,咱们拆解你遇到的两个核心问题:wait()抛出IllegalMonitorStateException的根源,以及isDone循环等待失效的原因,再给出针对性的解决方案。

为什么wait()会抛出异常?

Java里的wait()方法有个硬性要求:当前线程必须持有该对象的监视器锁(也就是必须在synchronized块或者synchronized方法内部调用)。你现在直接调用this.wait(),根本没获取到this的锁,JVM自然会抛出这个异常。

为什么isDone循环没生效?

大概率是isDone变量没加volatile修饰符。Java中普通变量的修改在多线程间不保证可见性:当onObjectReceived在Socket的接收线程里把isDone改成true时,你的主线程可能还在读取缓存里的旧值,导致循环一直卡着。


具体解决方案

方案1:正确使用wait()/notify()机制

先把exec方法里的wait()放到同步块里,确保获取到锁:

QueryIterator it = null;
try {
    this.connect();
    this.send(algorithmAndArgs);
    
    // 必须在synchronized块里调用wait()
    synchronized(this) {
        this.wait(); // 现在不会抛异常了
    }
    
    @SuppressWarnings("unused")
    ResultSet rs = ResultSetFactory.create(it, Arrays.asList(resultIdentifiers));
} catch (Exception e) {
    e.printStackTrace();
}
return it;

然后在Client类的onObjectReceived方法里,收到服务器响应后唤醒等待的线程:

@Override
public void onObjectReceived(Object o, Socket s) {
    // 这里先处理服务器返回的对象,给it赋值
    it = ...; // 注意it要保证线程安全,比如加volatile或者在同步块里操作
    
    // 唤醒等待的线程
    synchronized(this) {
        this.notify();
    }
}

小提示:如果有多个线程可能等待同一个对象,用notifyAll()代替notify()更稳妥,防止漏唤醒。

方案2:修复isDone循环等待的问题

如果不想用wait/notify,只需要给isDone加个volatile就行:

  1. 声明变量:
private volatile boolean isDone = false;
  1. 修改exec方法的循环:
QueryIterator it = null;
try {
    this.connect();
    this.send(algorithmAndArgs);
    
    // 循环等待,每次休眠100ms避免CPU空转
    while (!isDone) {
        Thread.sleep(100);
    }
    
    @SuppressWarnings("unused")
    ResultSet rs = ResultSetFactory.create(it, Arrays.asList(resultIdentifiers));
} catch (Exception e) {
    e.printStackTrace();
}
return it;
  1. 在onObjectReceived里标记完成:
@Override
public void onObjectReceived(Object o, Socket s) {
    it = ...; // 处理返回对象
    isDone = true;
}

方案3:用Java并发工具类(更优雅)

对于这种等待异步结果的场景,CountDownLatch或者CompletableFuture是更现代的选择,代码更简洁也不容易出错:

用CountDownLatch的例子:

QueryIterator it = null;
// 创建一个计数为1的门闩,收到响应后减1
CountDownLatch latch = new CountDownLatch(1);

try {
    this.connect();
    this.send(algorithmAndArgs);
    
    // 等待,直到门闩计数为0(最多等5秒,避免无限等待)
    latch.await(5, TimeUnit.SECONDS);
    
    @SuppressWarnings("unused")
    ResultSet rs = ResultSetFactory.create(it, Arrays.asList(resultIdentifiers));
} catch (Exception e) {
    e.printStackTrace();
}
return it;

然后在onObjectReceived里:

@Override
public void onObjectReceived(Object o, Socket s) {
    it = ...; // 处理返回对象
    latch.countDown(); // 计数减1,唤醒等待的线程
}

额外注意点

  • 不管用哪种方案,QueryIterator it的赋值一定要保证线程安全,比如加volatile修饰,或者在同步块里操作,防止主线程拿到未初始化的it。
  • 一定要加超时时间!比如wait(5000)或者latch.await(5, TimeUnit.SECONDS),不然如果服务器没响应,你的线程会一直卡着。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:42:09