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就行:
- 声明变量:
private volatile boolean isDone = false;
- 修改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;
- 在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
相关产品推荐
相关产品推荐

