网上有很多资料说 Kryo 只能在 Java 上使用,这点是不对的,事实上除 Java 外,Scala 和 Kotlin 这些基于 JVM 的语言同样可以使用 Kryo 实现序列化。

1.使用方法

(1)添加kyro依赖

<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.2.0</version>
</dependency>

(2)基本使用概况

Kryo 类会自动执行序列化。Output 类和 Input 类负责处理缓冲字节,并写入到流中。

public class HelloKryo {
static public void main(String[] args) throws Exception {
Kryo kryo = new Kryo();
kryo.register(SomeClass.class); SomeClass object = new SomeClass();
object.value = "Hello Kryo!"; Output output = new Output(new FileOutputStream("file.bin"));
kryo.writeObject(output, object);
output.close(); Input input = new Input(new FileInputStream("file.bin"));
SomeClass object2 = kryo.readObject(input, SomeClass.class);
input.close();
System.out.println(object2.value);
} static public class SomeClass {
String value;
}
}

(3)Kyro注册

和很多其他的序列化框架一样,Kryo 为了提供性能减小序列化结果体积,提供注册的序列化对象类的方式。在注册时,会为该序列化类生成 int ID,后续在序列化时使用 int ID 唯一标识该类型。注册的方式如下:



或者



可以明确指定注册类的 int ID,但是该 ID 必须大于等于 0。如果不提供,内部将会使用 int++的方式维护一个有序的 int ID 生成。

2.线程不安全

Kryo 不是线程安全的。每个线程都应该有自己的 Kryo 对象、输入和输出实例。

因此在多线程环境中,可以考虑使用 ThreadLocal 或者对象池来保证线程安全性。

ThreadLocal解决线程不安全

ThreadLocal 是一种典型的牺牲空间来换取并发安全的方式,它会为每个线程都单独创建本线程专用的 kryo 对象。对于每条线程的每个 kryo 对象来说,都是顺序执行的,因此天然避免了并发安全问题。

创建方法如下:

static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {
protected Kryo initialValue() {
Kryo kryo = new Kryo();
// 在此处配置kryo对象的使用示例,如循环引用等
return kryo;
};
}; Kryo kryo = kryos.get();

之后,仅需要通过 kryos.get() 方法从线程上下文中取出对象即可使用。

对象池解决线程不安全

「池」是一种非常重要的编程思想,连接池、线程池、对象池等都是「复用」思想的体现,通过将创建的“对象”保存在某一个“容器”中,以便后续反复使用,避免创建、销毁的产生的性能损耗,以此达到提升整体性能的作用。

Kryo 对象池原理也是如此。Kryo 框架自带了对象池的实现,整个使用过程不外乎创建池、从池中获取对象、归还对象三步,以下为代码实例。

// Pool constructor arguments: thread safe, soft references, maximum capacity
Pool<Kryo> kryoPool = new Pool<Kryo>(true, false, 8) {
protected Kryo create () {
Kryo kryo = new Kryo();
// Kryo 配置
return kryo;
}
}; // 获取池中的Kryo对象
Kryo kryo = kryoPool.obtain();
// 将kryo对象归还到池中
kryoPool.free(kryo);

参数分析:

第一个参数用于指定是否在 Pool 内部使用同步,如果指定为 true,则允许被多个线程并发访问。

第三个参数适用于指定对象池的大小的。

第二个参数设置为 true,Kryo 池将会使用 java.lang.ref.SoftReference 来存储对象。这允许池中的对象在 JVM 的内存压力大时被垃圾回收。Pool clean 会删除所有对象已经被垃圾回收的软引用。当没有设置最大容量时,这可以减少池的大小。当池子有最大容量时,没有必要调用 clean,因为如果达到了最大容量,Pool free 会尝试删除一个空引用。

创建完 Kryo 池后,使用 kryo 就变得异常简单了,只需调用 kryoPool.obtain() 方法即可,使用完毕后再调用 kryoPool.free(kryo) 归还对象,就完成了一次完整的租赁使用。

理论上,只要对象池大小评估得当,就能在占用极小内存空间的情况下完美解决并发安全问题。如果想要封装一个 Kryo 的序列化方法,可以参考如下的代码


public static byte[] serialize(Object obj) {
Kryo kryo = kryoPool.obtain();
// 使用 Output 对象池会导致序列化重复的错误(getBuffer返回了Output对象的buffer引用)
try (Output opt = new Output(1024, -1)) {
kryo.writeClassAndObject(opt, obj);
opt.flush();
return opt.getBuffer();
}finally {
kryoPool.free(kryo);
}
}

3.小结

相较于 JDK 自带的序列化方式,Kryo 的性能更快,并且由于 Kryo 允许多引用和循环引用,在存储开销上也更小。

只不过,虽然 Kryo 拥有非常好的性能,但其自身却舍去了很多特性,例如线程安全、对序列化对象的字段修改等。虽然这些弊端可以通过 Kryo 良好的扩展性得到一定的满足,但是对于开发者来说仍然具有一定的上手难度,不过这并不能影响其在 Java 中的地位。

最新文章

  1. 解决Java接口内部类的main()方法无法打印输出的问题
  2. 《与小卡特一起学Python》 Code5 for循环
  3. java.io.FileNotFoundException: D:\xxx\yyy (拒绝访问。)问题
  4. 使用PowerDesigner导出Word/HTML的一些配置
  5. hdu 2112 HDU Today (最短路,字符处理)
  6. UpdateLayeredWindow是炫效果的关键
  7. firebreath注册接口
  8. Android error:No CPU/ABI system image available for this target
  9. python 2.7 字符串处理
  10. JSP标准标签库(JSTL)--SQL标签库 sql
  11. 写个 Hello world - 前端从入坑到弃坑系列教程(1)
  12. Redux 学习总结
  13. [tomcat启动报错]registered the JDBC driver [com.alibaba.druid.proxy.DruidDriver] but failed to unregister it when the web application was stopped
  14. 广工十四届校赛 count 矩阵快速幂
  15. Floyd多源最短路
  16. steam Depot 生成与应用脚本
  17. Luogu 2147 洞穴勘测 - LCT
  18. 部署描述符(web.xml)和标注(annotation)
  19. Unique Paths II leetcode java
  20. 认识和使用Task

热门文章

  1. iptables介绍和基本使用
  2. PyCharm配置远程Docker环境
  3. 将vue+nodejs项目部署到服务器上(完整版)
  4. JS 学习笔记(一)常用的字符串去重方法
  5. git的介绍、git的功能特性、git工作流程、git 过滤文件、git多分支管理、远程仓库、把路飞项目传到远程仓库(非空的)、ssh链接远程仓库,协同开发
  6. 成熟企业级开源监控解决方案Zabbix6.2关键功能实战-下
  7. Codeforces Round #833 (Div. 2) A-D.md
  8. C#11之原始字符串
  9. 如何使用ModelBox快速提升AI应用性能?
  10. Go语言核心36讲29