我正在使用嵌入式Rhino的第三方应用程序中编写一段javascript(ecmascript)。应用程序可以启动多个Java线程来同时处理数据。似乎每个Java线程都会启动自己的嵌入式Rhino上下文,而后者又会运行我的脚本。
我的脚本的目的是从应用程序接收数据并使用它来维护特定文件的内容。我需要一个故障安全解决方案来处理我脚本的并发性。
到目前为止,我提出的是调用java并使用java.nio.channels.FileLock
。但是,文档here声明:
代表整个Java虚拟机保存文件锁。它们不适合控制同一虚拟机中多个线程对文件的访问。
果然,阻塞调用FileChannel.lock()
不会阻塞但会引发异常,导致以下丑陋的代码:
var count = 0;
while ( count < 100 )
{
try
{
var rFile = new java.io.RandomAccessFile(this.mapFile, "rw");
var lock = rFile.getChannel().lock();
try
{
// Here I do whatever the script needs to do with the file
}
finally
{
lock.release();
}
rFile.close();
break;
} catch (ex) {
// This is reached whenever another instance has a lock
count++;
java.lang.Thread.sleep( 10 );
}
}
问:我如何以安全可靠的方式解决这个问题?
我见过有关Rhino sync()
与Java synchronized
类似的帖子,但这似乎不适用于多个Rhino实例。
更新
我尝试过将Synchronizer
与org.mozilla.javascript.tools.shell.Global
一起用作模板的建议:
function synchronize( fn, obj )
{
return new Packages.org.mozilla.javascript.Synchronizer(fn).call(obj);
}
接下来,我按如下方式使用此功能:
var mapFile = new java.io.File(mapFilePath);
// MapWriter is a js object
var writer = new MapWriter( mapFile, tempMap );
var on = Packages.java.lang.Class.forName("java.lang.Object");
// Call the writer's update function synchronized
synchronize( function() { writer.update() } , on );
但是我看到两个线程同时进入update()
功能。我的代码出了什么问题?
答案 0 :(得分:1)
根据Rhino的嵌入方式,有两种可能性:
如果代码在Rhino shell中执行,请使用sync(f,lock)
函数将函数转换为在第二个参数或其调用的this
对象上同步的函数如果没有第二个参数。 (早期版本只有单参数方法,因此,除非您的第三方应用程序使用最新版本,否则您可能需要使用它或自己滚动;请参阅下文。)
如果应用程序没有使用Rhino shell,但使用的是不包含并发工具的自定义嵌入,那么您需要推出自己的版本。 sync
的源代码是一个很好的起点(请参阅Global和Synchronizer的源代码;您应该可以使用Synchronizer,它几乎是开箱即用的全球使用它)。
问题可能是您尝试同步的对象不是跨上下文共享,而是通过嵌入或其他内容多次创建。如果是这样,您可能需要使用某种黑客,特别是如果您无法控制嵌入。如果您无法控制嵌入,您可以使用某种类型的VM全局对象进行同步,例如Runtime.getRuntime()
或其他东西(我无法想到任何我立即知道的单个对象,但是我怀疑其中有几个像Runtime
这样的单身API是。)
要同步的东西的另一个候选者就像Packages.java.lang.Class.forName("java.lang.Object")
,它应该引用所有上下文中的同一个对象(Object
类),除非嵌入的类加载器设置非常不寻常。