分享

关于在java上使用lua脚本

 quasiceo 2015-01-17
Lua是一种小巧的脚本语言,现在经常被用于游戏中。

如果要在Java中使用Lua需要第三方库,如果搜索的话,一般搜到的都是LuaJava。LuaJava并不是一个纯Java的实现,它需要通过native方法调用C库,依赖于Lua 5.1。官网上可以下载到编译好的win32版LuaJava,其他平台的可以自己用源码进行编译。不幸的是它本身有一些bug,会导致JVM崩溃。而且LuaJava从2007年之后也不再更新了,没人维护。

除了LuaJava之外还有一些其他的Lua脚本引擎,比如LuaJ。LuaJ是纯Java实现的Lua解释器,没有native方法,因此相对健壮一些,不会由于一些错误轻易的导致JVM crash,而且还支持JSR-223。

现在LuaJ的release版本是2.0.3,2这个系列的版本是有线程安全问题的。我们可以写段代码来试一下。
Java代码  收藏代码
  1. package net.szh;  
  2.   
  3. import java.util.concurrent.CountDownLatch;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6.   
  7. import javax.script.ScriptEngine;  
  8. import javax.script.ScriptEngineManager;  
  9. import javax.script.ScriptException;  
  10.   
  11. public class TestLuaj {  
  12.   
  13.     public static void main(String[] args) throws Exception {  
  14.         int threadCount = 4;  
  15.         final int runCount = 100000;  
  16.         ExecutorService exec = Executors.newCachedThreadPool();  
  17.         final CountDownLatch cdl = new CountDownLatch(threadCount);  
  18.         for (int i = 0; i < threadCount; i++) {  
  19.             exec.execute(new Runnable() {  
  20.   
  21.                 @Override  
  22.                 public void run() {  
  23.                     ScriptEngineManager mgr = new ScriptEngineManager();  
  24.                     ScriptEngine engine = mgr.getEngineByName("luaj");  
  25.                     try {  
  26.                         for (int i = 0; i < runCount; i++) {  
  27.                             engine.eval("local i = 0; assert(i)");  
  28.                         }  
  29.                     } catch (ScriptException e) {  
  30.                         e.printStackTrace();  
  31.                     } finally {  
  32.                         cdl.countDown();  
  33.                     }  
  34.                 }  
  35.             });  
  36.         }  
  37.         cdl.await();  
  38.         exec.shutdown();  
  39.     }  
  40.   
  41. }  

执行后,有可能会出现如下的异常信息:
Java代码  收藏代码
  1. Exception in thread "pool-1-thread-3" java.lang.ArrayIndexOutOfBoundsException: -1  
  2.     at org.luaj.vm2.LuaThread$CallStack.onReturn(Unknown Source)  
  3.     at org.luaj.vm2.LuaClosure.execute(Unknown Source)  
  4.     at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)  
  5.     at org.luaj.vm2.LuaClosure.invoke(Unknown Source)  
  6.     at org.luaj.vm2.script.LuaScriptEngine$CompiledScriptImpl.eval(Unknown Source)  
  7.     at org.luaj.vm2.script.LuaScriptEngine.eval(Unknown Source)  
  8.     at org.luaj.vm2.script.LuaScriptEngine.eval(Unknown Source)  
  9.     at org.luaj.vm2.script.LuaScriptEngine.eval(Unknown Source)  
  10.     at net.szh.TestLuaj$1.run(TestLuaj.java:27)  
  11.     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)  
  12.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)  
  13.     at java.lang.Thread.run(Thread.java:662)  

官方的jar包没把行号编译进去,我们可以自己打个jar包。
Java代码  收藏代码
  1. Exception in thread "pool-1-thread-3" java.lang.ArrayIndexOutOfBoundsException: -1  
  2.     at org.luaj.vm2.LuaThread$CallStack.onReturn(LuaThread.java:345)  
  3.     at org.luaj.vm2.LuaClosure.execute(LuaClosure.java:506)  
  4.     at org.luaj.vm2.LuaClosure.onInvoke(LuaClosure.java:176)  
  5.     at org.luaj.vm2.LuaClosure.invoke(LuaClosure.java:168)  
  6.     at org.luaj.vm2.script.LuaScriptEngine$CompiledScriptImpl.eval(LuaScriptEngine.java:207)  
  7.     at org.luaj.vm2.script.LuaScriptEngine.eval(LuaScriptEngine.java:111)  
  8.     at org.luaj.vm2.script.LuaScriptEngine.eval(LuaScriptEngine.java:107)  
  9.     at org.luaj.vm2.script.LuaScriptEngine.eval(LuaScriptEngine.java:95)  
  10.     at net.szh.TestLuaj$1.run(TestLuaj.java:27)  
  11.     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)  
  12.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)  
  13.     at java.lang.Thread.run(Thread.java:662)  

对照源代码可以看到是因为内部共享了一个LuaFunction数组和数组索引的计数器,导致了此问题。

不过LuaJ已经有3.0版了,不过是beta版的。我第一次看到的时候是3.0-beta1,在3.0的描述中有提到对线程安全做了改进。用 3.0-beta1 跑一下上边的代码可以发现不再有异常抛出。这是一个较为可用的版本。

今年年初的时候,LuaJ发布了 3.0-beta2 版,试用了一下,发现有字符编码问题,可以跑下代码
Java代码  收藏代码
  1. package net.szh;  
  2.   
  3. import javax.script.ScriptEngine;  
  4. import javax.script.ScriptEngineManager;  
  5.   
  6. public class TestLuaj {  
  7.   
  8.     public static void main(String[] args) throws Exception {  
  9.         ScriptEngineManager mgr = new ScriptEngineManager();  
  10.         ScriptEngine engine = mgr.getEngineByName("luaj");  
  11.         engine.eval("--啦啦啦 local i = 0; assert(i)");  
  12.     }  
  13.   
  14. }  

执行后,会出现如下的异常信息:
Java代码  收藏代码
  1. Exception in thread "main" javax.script.ScriptException: eval threw javax.script.ScriptException: load script: java.lang.ArrayIndexOutOfBoundsException: 32  
  2.     at org.luaj.vm2.script.LuaScriptEngine.compile(LuaScriptEngine.java:94)  
  3.     at org.luaj.vm2.script.LuaScriptEngine.eval(LuaScriptEngine.java:112)  
  4.     at org.luaj.vm2.script.LuaScriptEngine.eval(LuaScriptEngine.java:106)  
  5.     at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)  
  6.     at net.szh.TestLuaj.main(TestLuaj.java:11)  

我已经提了bug,并且有人已经找到了问题所在。所以现在还是只有 3.0-beta1 相对可用。

--------------------
现在已经有了3.0版,已经修复了UTF-8编码的bug。
分享到:

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多