Java调用Lua脚本(热载实现)
前言:
Lua作为解析执行的脚本语言, 往往是易变逻辑编写的首选语言, 尤其是在游戏领域. C/C++和Lua的结合, 往往了标配. 比如Redis, Nginx其对Lua的支持, 也是杠杠的. 当然Lua也可以作为规则引擎中的规则编写语言. 本文对Java调用Lua(Luaj)的实现机制, 做下简单的介绍.
Luaj简介:
Luaj是Java调用Lua的一种实现方式, 其是构建一个虚拟机解析执行Lua脚本来实现的, 这和Groovy的方式有所不同.
这是Luaj的官网, http://www.luaj.org/luaj/3.0/README.html.
它是针对5.2.x的lua版本的解析器, 其Luaj库的编写是通过JavaCC来实现的.
简单示例:
集合Luaj, 可以通过Maven进行如下配置:
1 2 3 4 5 | < dependency > < groupId >org.luaj</ groupId > < artifactId >luaj-jse</ artifactId > < version >3.0.1</ version > </ dependency > |
Luaj的一个简单的示例程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import org.luaj.vm2.Globals; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.JsePlatform; public class TestLuaJ { public static void main(String[] args) { String luaStr = "print 'hello,world!'" ; Globals globals = JsePlatform.standardGlobals(); LuaValue chunk = globals.load(luaStr); chunk.call(); } } |
注: Globals继承LuaValue对象,LuaValue对象用来表示在Lua语言的基本数据类型,比如:Nil,Number,String,Table,userdata,Function等。尤其要注意LuaValue也表示了Lua语言中的函数。所以,对于Lua语言中的函数操作都是通过LuaValue来实现的.
其输出的结果:
hello,world!
原理初探:
根据官方的说法, Luaj在包装执行具体的Lua代码时, 有三种不同的模式.
1). 纯脚本解析执行(不选用任何Compiler)
2). To Lua字节码(LuaC, lua-to-lua-bytecode compiler)
3). To Java字节码(LuaJC, lua-to-java-bytecode compiler)
其中LuaC是默认的选用Compiler.
依据官方的介绍:
不使用LuaC的方法是, 则不调用如何行:
org.luaj.vm2.compiler.LuaC.install(globals);
而使用LuaJC的方法, 则是调用
org.luaj.vm2.jse.luajc.LuaJC.install(globals);
可惜, 笔者在自己测试过程中, 遇到了异常(org.luaj.vm2.LuaError: No compiler.), 好尴尬:
性能评估:
对Lua解析的代码进行简单的性能评估:
其对同样的逻辑代码:
1 2 3 4 | int a = 0 ; for ( int i = 0 ; i < 10000 ; i++ ) { a = a + i; } |
执行10000次, 具体对比耗时值.
整体的测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import org.luaj.vm2.Globals; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.JsePlatform; public class TestLuaJPerf { public static void main(String[] args) { int iterNum = 10000 ; // *) java 模式运行 long beg = System.currentTimeMillis(); for ( int j = 0 ; j < iterNum; j++ ) { int a = 0 ; for ( int i = 0 ; i < 10000 ; i++ ) { a = a + i; } } long end = System.currentTimeMillis(); System.out.println(String.format( "Java consume: %dms" , end - beg)); // *) Lua脚本解析执行 String luaStr = "a = 0; for i = 0, 10000, 1 do a = a + i; end" ; Globals globals = JsePlatform.standardGlobals(); LuaValue chunk = globals.load(luaStr); beg = System.currentTimeMillis(); for ( int i = 0 ; i < iterNum; i++ ) { chunk.call(); } end = System.currentTimeMillis(); System.out.println(String.format( "Lua consume: %dms" , end - beg)); } } |
测试结果如下:
Java consume: 10ms
Lua consume: 10249ms
几乎1000倍的差异, 这个性能对比, 差异有些大, Lua确实慢的不止半点(可能和Luaj的具体实现也有些关系), 因此从这方面来说, Java+Groovy的结合, 比Java+Lua的结合更有优势.
线程安全:
Luaj中的Globals对象不是线程安全的, 因此最佳实践是每个线程一个Globals对象.
事实上, 可以采用ThreadLocal的方式来存储该对象.
因为是对象, 而不是Class, 其和Groovy编译的Script类, 其实现思路是本质区别的.
总结:
个人对Luaj的认识还是有些肤浅, 没有深入地去研究, 所以可能这边的一些结论可能不准确. 同时Luaj对Lua脚本的支持, 到什么程度, 其实也是一个问号. 不管怎么样, 能对Luaj能有一个初步的认识, 也是好事.
posted on 2018-03-02 14:02 mumuxinfei 阅读(33177) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?