2015-06-24 2 views
0

Я пишу часть javascript (ecmascript) в стороннем приложении, которое использует встроенный Rhino. Приложение может запускать несколько потоков Java для обработки данных одновременно. Кажется, что каждый поток Java начинает свой собственный встроенный контекст Rhino, который, в свою очередь, запускает мой скрипт.Несколько потоков Rhino (java) управляют одним и тем же файлом

Целью моего сценария является получение данных из приложения и использование его для хранения содержимого определенного файла. Мне нужно отказоустойчивое решение для обработки параллелизма из моего скрипта.

До сих пор, что я придумал, это обратиться к 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); 
    }  
} 

Q: Как я могу решить эту проблему в безопасной и надежной манере?

Я видел сообщения о Rhino sync(), похожие на Java synchronized, но это не работает между несколькими экземплярами Rhino.

UPDATE

Я попытался предложение использовать 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

sync() работает между всеми контекстами в одном экземпляре Rhino и во всех экземплярах Rhino, запущенных в одной виртуальной машине, что, как я предполагаю, является тем, о чем мы говорим (он использует синхронизацию Java, а также может координировать с встроенные средства параллелизма Java). –

+0

Мое предположение, не будучи в состоянии проверить вашу установку (и без тестирования моей теории), заключается в том, что функцию нельзя вызвать с помощью класса java.lang.Object' как 'this'. Попробуйте 'return new Packages.org.mozilla.javascript.Synchronizer (fn, obj)', а затем вызовите возвращаемую функцию: 'synchronize (function() {writer.update()}, on)();' и посмотреть, будет ли это лучше. –

+0

Я получаю сообщение об ошибке 'org.mozilla.javascript.EvaluatorException: Java-конструктор для« org.mozilla.javascript.Synchronizer »с аргументами function, java.lang.Class« не найден ». Глядя на источник, на самом деле нет конструктор для этого, я должен пройти (Scriptable, Object). – mvreijn

ответ

1

В зависимости, как Rhino встраивается, есть две возможности:

  1. Если код выполняется в Rhino оболочке, используйте функцию sync(f,lock), чтобы включить функцию в функцию, которая синхронизируется по второму аргументу, или в объекте его вызова, если второй аргумент отсутствует. (В более ранних версиях был только метод с одним аргументом, поэтому, если ваше стороннее приложение не использует последнюю версию, вам может понадобиться использовать его или перевернуть самостоятельно, см. Ниже.)

  2. Если приложение не использует Rhino, но с использованием пользовательского внедрения, которое не включает инструменты параллелизма, вам нужно будет запустить свою собственную версию. Исходный код для sync является хорошей отправной точкой (см. Исходный код для Global и Synchronizer; вы должны иметь возможность использовать Synchronizer в значительной степени из-за коробки так же, как использует Global).

Вполне возможно, что проблема заключается в том, что объект, на котором вы пытаетесь синхронизировать не разделяется в зависимости от контекста, но создаются несколько раз вложения или что-то. Если это так, вам может потребоваться какой-то хак, особенно если вы не контролируете внедрение. Если у вас нет контроля над вложением, вы можете использовать какой-то VM-глобальный объект для синхронизации, например Runtime.getRuntime() или что-то в этом роде (я не могу думать о том, что я сразу знаю, это отдельные объекты, но я подозреваю, что некоторые из них с одноплодной API-интерфейсы, такие как Runtime есть.)

Другого кандидат на то, на котором для синхронизации будет нечто вроде Packages.java.lang.Class.forName("java.lang.Object"), которые должны относиться к одной и тому же объекту (Object класса) во всех контекстах, если установка загрузчик классов вложения никогда чрезвычайно необычна ,

+0

Я точно знаю, что 'sync()' недоступен (ergo custom embedding). Моя первая попытка с 'Синхронизатором' не работала, поэтому я предположил, что это только для одного экземпляра Rhino. Я посмотрю исходный код «Global» и даю ему еще одну попытку. – mvreijn

+0

Не могли бы вы проверить мой обновленный пост? Я явно ошибаюсь. – mvreijn