Multithreaded JavaScript with J2V8

Multithreaded JavaScript with J2V8

J2V8 provides a tight binding between Java and the V8 JavaScript engine. Using JNI, you can execute JS scripts on V8, get and set values, register callbacks and invoke JavaScript functions — all from Java. However, because of the subtleties related to multiple threads, J2V8 requires that all interactions happen from the same thread. Because JavaScript itself is single threaded, this works out ok.

But what if you want to implement something like WebWorkers? You would likely create a new V8Runtime and execute a script on a separate thread — but this will fail! Starting with J2V8 3.0 you can now interact with J2V8 from different threads, with one rule: All interactions with a V8Runtime must be from the thread that instantiates the Runtime. This means that you can create a V8Runtime for each thread in the system, and each thread can interact with its own V8Runtime independent of each other. A snapshot release is currently available in Maven Central.

<pre lang="xml">
  <dependencies>
    <dependency>
      <groupId>com.eclipsesource.j2v8</groupId>
      <artifactId>j2v8_win32_x86</artifactId>
      <version>3.0.0-SNAPSHOT</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</pre>

In short, it means you can now do this (although don’t forget to release the runtime when you’re done):

<pre lang="java">
new Thread(() -> V8.createV8Runtime().executeScript("...")).start();
</pre>

To test this, we created a merge sort that used separated V8Runtimes at each sort step. At each step, the list would be divided in half and two new V8Runtimes were created. Each V8Runtime executed the merge sort algorithm in parallel, and the results were merged together. Just for fun, we ran 10 parallel merge sorts.

<pre lang="javascript">
function sort(data) {
  if ( data.length === 1 ) {
    return [data[0]];
  } else if (data.length === 2 ) {
    if ( data[1] < data[0] ) {
      return [data[1],data[0]];
    } else {
      return data;
    }
  }
  var mid = Math.floor(data.length / 2);
  var first = data.slice(0, mid);
  var second = data.slice(mid);
  return merge(_sort( first ), _sort( second ) );
};
</pre>

We managed to remove all shared data structures between different instances of the V8Runtime, allowing the entire system to run without locks. This greatly improved performance.

The source code for the merge sort example is available on GitHub.

For more information on J2V8 and for future updates, follow me on Twitter.

2 Comments