多个 Rhino (java) 线程操作同一个文件
Multiple Rhino (java) threads manipulate the same file
我正在使用嵌入式 Rhino 的第 3 方应用程序中编写一段 java 脚本 (ecmascript)。应用程序可能会启动多个 Java 线程来并发处理数据。似乎每个 Java 线程都启动了自己的嵌入式 Rhino 上下文,后者又运行我的脚本。
我的脚本的目的是从应用程序接收数据并使用它来维护特定文件的内容。我需要一个故障安全解决方案来处理脚本中的并发。
到目前为止,我想到的是调用java并使用java.nio.channels.FileLock
。但是,文档 here 指出:
File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine.
果然,阻塞调用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()
函数。我的代码有什么问题?
根据 Rhino 的嵌入方式,有两种可能性:
如果代码在 Rhino shell 中执行,使用 sync(f,lock)
函数将函数转换为在第二个参数上同步的函数,或者在 this
如果第二个参数不存在,它的调用对象。 (早期版本只有一个参数方法,因此除非您的第三方应用程序使用最新版本,否则您可能需要使用该版本或自行推出;见下文。)
如果应用程序未使用 Rhino shell,而是使用不包含并发工具的自定义嵌入,则您需要推出自己的版本。 sync
的源代码是一个很好的起点(请参阅 Global and Synchronizer 的源代码;您应该能够像 Global 使用它一样开箱即用地使用 Synchronizer)。
问题可能是您尝试同步的对象不是跨上下文共享的,而是通过嵌入或其他方式多次创建的。如果是这样,您可能需要使用某种黑客手段,尤其是当您无法控制嵌入时。如果你无法控制嵌入,你可以使用某种 VM 全局对象来同步,比如 Runtime.getRuntime()
之类的(我想不出任何我立即知道的是单个对象,但我怀疑其中有几个像 Runtime
这样的单例 API。)
同步对象的另一个候选对象类似于 Packages.java.lang.Class.forName("java.lang.Object")
,它应该在所有上下文中引用相同的对象(Object
class),除非嵌入的 class 加载程序设置非常不寻常。
我正在使用嵌入式 Rhino 的第 3 方应用程序中编写一段 java 脚本 (ecmascript)。应用程序可能会启动多个 Java 线程来并发处理数据。似乎每个 Java 线程都启动了自己的嵌入式 Rhino 上下文,后者又运行我的脚本。
我的脚本的目的是从应用程序接收数据并使用它来维护特定文件的内容。我需要一个故障安全解决方案来处理脚本中的并发。
到目前为止,我想到的是调用java并使用java.nio.channels.FileLock
。但是,文档 here 指出:
File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine.
果然,阻塞调用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()
函数。我的代码有什么问题?
根据 Rhino 的嵌入方式,有两种可能性:
如果代码在 Rhino shell 中执行,使用
sync(f,lock)
函数将函数转换为在第二个参数上同步的函数,或者在this
如果第二个参数不存在,它的调用对象。 (早期版本只有一个参数方法,因此除非您的第三方应用程序使用最新版本,否则您可能需要使用该版本或自行推出;见下文。)如果应用程序未使用 Rhino shell,而是使用不包含并发工具的自定义嵌入,则您需要推出自己的版本。
sync
的源代码是一个很好的起点(请参阅 Global and Synchronizer 的源代码;您应该能够像 Global 使用它一样开箱即用地使用 Synchronizer)。
问题可能是您尝试同步的对象不是跨上下文共享的,而是通过嵌入或其他方式多次创建的。如果是这样,您可能需要使用某种黑客手段,尤其是当您无法控制嵌入时。如果你无法控制嵌入,你可以使用某种 VM 全局对象来同步,比如 Runtime.getRuntime()
之类的(我想不出任何我立即知道的是单个对象,但我怀疑其中有几个像 Runtime
这样的单例 API。)
同步对象的另一个候选对象类似于 Packages.java.lang.Class.forName("java.lang.Object")
,它应该在所有上下文中引用相同的对象(Object
class),除非嵌入的 class 加载程序设置非常不寻常。