org.jruby.Rubyをシンクロして使う。
2008/09/04
java
jruby
JRubyです。
org.jruby.Rubyオブジェクトを使いまわしてスクリプトを実行する際に、グローバル変数を使ってJRubyに値を渡すとき、org.jruby.Rubyをシンクロしないといけないよというお話。
以下、スレッドをぽこぽこ起こして、org.jruby.Rubyにグローバル変数経由で値をわたすと意図せずまざります。
org.jruby.Rubyを生成するコストはばかにならないので、newしてぽこぽこ生成したくはないです。
あたりまえといえばあたりまえですが、使うorg.jruby.Rubyをsynchronizedブロックにしてしまいます。
テスト用のjrubyのスクリプト
p $a + ' ' + $b + ' ' + $c + ' ' + $d + ' ' + $e + ' ' + $f + ' ' + $g + ' ' + $h
このスクリプトの意図はグローバル変数が途中で書き換わると、値に一貫性がなくなることをチェックするためです。
シンクロしないとものの見事に、一貫性がなくなることが確認されます。
以下うまくいったコード
package test_jruby;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;
public class Test_multi {
public static void main(String[] args) throws IOException {
final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {
final int FI = i;
new Thread(new Runnable() {
@Override
public void run() {
String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });
System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}
/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {
synchronized (ruby) {
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {
ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}
}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();
}
}
}
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;
public class Test_multi {
public static void main(String[] args) throws IOException {
final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {
final int FI = i;
new Thread(new Runnable() {
@Override
public void run() {
String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });
System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}
/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {
synchronized (ruby) {
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {
ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}
}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();
}
}
}
以下うまくいかなかったコード
org.jruby.RubyのdefineVariableをする間隔で、Tread.sleepさせるとさらにまざる感じがします。
package test_jruby;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;
public class Test_multi {
public static void main(String[] args) throws IOException {
final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {
final int FI = i;
new Thread(new Runnable() {
@Override
public void run() {
String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });
System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}
/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {
// synchronized (ruby) {
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {
ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}
}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();
// }
}
}
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;
public class Test_multi {
public static void main(String[] args) throws IOException {
final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {
final int FI = i;
new Thread(new Runnable() {
@Override
public void run() {
String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });
System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}
/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {
// synchronized (ruby) {
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {
ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}
}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();
// }
}
}
: