使用 Kryo 完成 序列化和反序列化,并使用ThreadLocal解决线程不安全问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 Kryo 完成 序列化和反序列化,并使用ThreadLocal解决线程不安全问题相关的知识,希望对你有一定的参考价值。

(目录)


基于kryo完成序列化和反序列化

1. Kryo的使用

Step01:定义mail类:

package com.java.serializable;
 
import java.io.Serializable;
import java.util.Date;
 
public class Mail implements Serializable
	private static final long serialVersionUID = 6599166688654530165L;
	private Integer id;
	private String title;
	private String content;
	private Date createdTime;
	public Integer getId() 
		return id;
	
	public void setId(Integer id) 
		this.id = id;
	
	public String getTitle() 
		return title;
	
	public void setTitle(String title) 
		this.title = title;
	
	public String getContent() 
		return content;
	
	public void setContent(String content) 
		this.content = content;
	
	public Date getCreatedTime() 
		return createdTime;
	
	public void setCreatedTime(Date createdTime) 
		this.createdTime = createdTime;
	
	@Override
	public String toString() 
		return "Mail [id=" + id + ", title=" + title + ", content=" + content + ", createdTime=" + createdTime + "]";
	
 

Step02:添加依赖

<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>5.0.0-RC4</version>
</dependency>

Step03:编写测试类

package com.java.serializable;
import java.util.Date;
 
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
 
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
public class TestSerializable06 
	public static void main(String[] args) 
		Mail m=new Mail();
		m.setId(100);
		m.setTitle("test");
		m.setContent("this is test content");
		m.setCreatedTime(new Date());
		//基于Kryo框架将对象序列化
		Kryo kryo=new Kryo();
		//将默认类的自动注册功能关闭(默认会将类全名序列化)
		kryo.setRegistrationRequired(false);
		//kryo.register(Mail.class);
		//kryo.register(Date.class);
		ByteArrayOutputStream bos=//内置可扩容数组
		new ByteArrayOutputStream();
		Output output=new Output(bos);
		kryo.writeObject(output, m); //写入null时会报错
		output.close();
		System.out.println("序列化ok");
		//基于Kryo框架将对象反序列化
		byte[] data=bos.toByteArray();
		Input input=new Input(data);
		Mail m2=kryo.readObject(input,Mail.class); //读出null时会报错
		input.close();
		System.out.println(m2);
		
	

结果:

序列化ok
Mail [id=100, title=test, content=this is test content, createdTime=Mon Nov 11 14:15:35 CST 2019]

2. 工具类

可将如上序列化方法进行封装,写到序列化工具类中,例如:

public class KryoSerializer 
    private static final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> 
		Kryo kryo = new Kryo();
		kryo.setReferences(true);//检测循环依赖,默认值为true,避免版本变化显式设置
		kryo.setRegistrationRequired(false);//默认值为true,避免版本变化显式设置
		((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
			.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());//设定默认的实例化器
		return kryo;
	);

	public byte[] serialize(Object obj) 
		Kryo kryo = getKryo();
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		Output output = new Output(byteArrayOutputStream);
		kryo.writeClassAndObject(output, obj);
		output.close();
		return byteArrayOutputStream.toByteArray();
	

	public <T> T deserialize(byte[] bytes) 
		Kryo kryo = getKryo();
		Input input = new Input(new ByteArrayInputStream(bytes));
		return (T) kryo.readClassAndObject(input);
	

	private Kryo getKryo() 
		return kryoLocal.get();
	


编写测试类:

package com.java.serializable;
import java.io.IOException;
import java.util.Date;
public class TestSerializable07 
	public static void main(String[] args)throws IOException 
		Mail m=new Mail();
		m.setId(100);
		m.setTitle("test");
		m.setContent("this is test content");
		m.setCreatedTime(new Date());
		//基于Kryo框架将对象序列化
		byte[] array=
		KryoSerializer.serializable(m);
		System.out.println("序列化OK,array.length="+array.length);
		//基于Kryo框架将对象反序列化
		Mail m2=
		KryoSerializer.deserialization(array,Mail.class);
	    System.out.println(m2);
	

运行结果:

序列化OK,array.length=49
Mail [id=100, title=test, content=this is test content, createdTime=Mon Nov 11 18:04:03 CST 2019]

3. 两种读写方式

根据是否写入class类型分为两种方式, 这里特别指出这里的的class指的是读写对象的class, 如果读写的是有嵌套类型对象,则不管采用哪种方式, 子类型class都会序列化.

3.1 只写实例信息

  • 知道class且对象不为null
kryo.writeObject(output, someObject);
    // ...
SomeClass someObject = kryo.readObject(input, SomeClass.class);
  • 知道class且对象可能为null
kryo.writeObjectOrNull(output, someObject);
    // ...
SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);

3.2 同时写入class类型和实例信息(RPC场景)

class未知且对象可能为null, 但这种场景, 会多占用空间. 这种方式是我们在RPC中应当使用的方式

kryo.writeClassAndObject(output, object);
    // ...
    Object object = kryo.readClassAndObject(input);
    if (object instanceof SomeClass) 
       // ...
    

4. 相关配置

register

  • 类注册

kryo支持通过类注册, 注册会给每一个class一个int类型的Id相关联,这显然比类名称高效,但同时要求反序列化的时候的Id必须与序列化过程中一致。这意味着注册的顺序非常重要。

kryo.register(SomeClassA.class);
kryo.register(SomeClassB.class);

但是由于现实原因,同样的代码,同样的Class在不同的机器上注册编号任然不能保证一致,所以多机器部署时候反序列化可能会出现问题。

所以kryo默认会开启类注册(version:5.0.2),可以通过kryo.setRegistrationRequired(false)关闭, 关闭后Kryo会根据类型去loadClass关联

kryo.setRegistrationRequired(false);//一般设置为false

解决线程不安全

由于Kryo线程不安全, 意味着每次序列化和反序列化时都需要实例化一次,借助ThreadLocal来维护以保证其线程安全

private static final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() 
    protected Kryo initialValue() 
        Kryo kryo = new Kryo();
        // configure kryo instance, customize settings
        return kryo;
    ;
;
// Somewhere else, use Kryo
Kryo k = kryos.get();
...

或者使用kryo提供的pool:

public KryoPool newKryoPool() 
        return new KryoPool.Builder(() -> 
            final Kryo kryo = new Kryo();
            kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
                    new StdInstantiatorStrategy()));
            return kryo;
        ).softReferences().build();


知识补充:ThreadLocal

ThreadLocal原理及详解参考文章: https://www.cnblogs.com/dolphin0520/p/3920407.html


以上是关于使用 Kryo 完成 序列化和反序列化,并使用ThreadLocal解决线程不安全问题的主要内容,如果未能解决你的问题,请参考以下文章

java——解决java.io.StreamCorruptedException: invalid stream header: xxx

Kryo 使用指南

Flink中使用Kryo序列化器的注意事项

无法使用 spark kryo 序列化

深入浅出Spring原理及实战「开发实战系列」分析探究RedisTemplate的序列化和反序列化+泛型机制

高性能的序列化与反序列化:kryo的简单使用