今天使用j2v8在java中执行JavaScript代码时,遇到了一个错误:
Exception in thread "Thread-2" java.lang.Error: Invalid V8 thread access
at com.eclipsesource.v8.V8Locker.checkThread(V8Locker.java:56)
at com.eclipsesource.v8.V8.checkThread(V8.java:676)
一个简单的报错示例:
@Test
public void test() throws InterruptedException {
AtomicReference<V8> v8 = new AtomicReference<>();
new Thread(() -> {
v8.set(V8.createV8Runtime());
}).start();
Thread.sleep(5000);
v8.get().executeScript("const i = 0;"); // 此处报错
v8.get().release();
}
跟踪checkThread方法:
public void checkThread() {
if (this.thread != Thread.currentThread()) {
throw new Error("Invalid V8 thread access");
}
}
发现在比对 this.thread 与 Thread.currentThread 是否一致,Thread.currentThread 是当前正在执行的线程,而 this.thread 则是V8Runtime创建时的线程,当两个线程不一致时就会抛出这个错误,而 this.thread 存在于V8Locker,v8locker提供两个跟 this.thread 相关的方法,release和acquire:
public synchronized void acquire() {
if (this.thread != null && this.thread != Thread.currentThread()) {
throw new Error("Invalid V8 thread access");
} else {
this.thread = Thread.currentThread();
}
}
public synchronized void release() {
this.checkThread();
this.thread = null;
}
所以可以得出解决方案,在创建v8Runtime的线程中释放v8Locker中锁定的线程,然后在使用v8的线程中将当前线程绑定到v8Locker中。
上边报错示例的解决方案:
@Test
public void test() throws InterruptedException {
AtomicReference<V8> v8 = new AtomicReference<>();
new Thread(() -> {
V8 v=V8.createV8Runtime();
v.getLocker().release(); // 在创建runtime的线程中release
v8.set(v);
}).start();
Thread.sleep(5000);
v8.get().getLocker().acquire();// 在使用runtime的线程中acquire
v8.get().executeScript("const i = 0;");
v8.get().release();
}
注:release和acquire二者缺一不可。