Open zhaoguozhenjava opened 8 months ago
DecoderMap操作都加锁的,应该不存在并发问题。
如果你自己调用了register,那么就是想定制自己的序列化策略,所以此时initDefaultDecoder不再生效,你应该把自己需要的decoder都注册一下。
没有主动调用,用的Kryo5ValueDecoder, -153049664就是Kryo5序列化反序列化的标记。 机器刚启动的时候反序列化的时候报no decoder for identity number:-153049664;如果正常初始化的话拿到的应该就是Kryo5ValueDecoder发序列化,不应该有问题; 2.7.2和2.7.3的版本都没有加锁的; 最新的代码里是有锁,但是锁是在读volatile变量inited之后的,所以正常并发的情况下有可能读到了未完全初始化的数据,导致报异常
以前没锁但是有synchronized,一样的。没看出这里有并发问题。
initDefaultDecoder方法没有锁啊,两个线程可以同时进入initDefaultDecoder方法; A线程进入了synchronized块里,且执行了一行register(SerialPolicy.IDENTITY_NUMBER_JAVA, defaultJavaValueDecoder()); 此时B线程执行if (inited) 将会返回true,但是A线程的initDefaultDecoder方法还没有执行完毕,其余的valueDecoder还没有注册进去,导致B线程拿不到valueDecoder
synchronized代码块怎么会有两个线程同时进去?
就算这里没有加任何线程安全措施,发生并发问题的概率也是极低的。你的问题如果是每次都出现的,应该从别的地方找原因。 要么是自己调用了register没有注册KRYO5,要么是低版本的jetcache(没有注册kryo5反序列化器)读了一个高版本jetcache写进去的kryo5方式序列化的数据。
synchronized代码块不会有两个线程进入,但是initDefaultDecoder方法可以,initDefaultDecoder方法没有加同步控制; 概率是比较低,就第一次启动的时候报错,jetcache版本一直没变过; 我这边用了线程池多个线程同时去获取了缓存,就存在了竞争,出来了并发的问题
如果register我自己调用的话就不来这里提问啦,肯定没有调用,谢谢
我不明白你的意思,我也没看出来现在的代码有什么问题(除了上面提到的两个问题:用户自己调用register没有注册完全,或者版本问题)。
还有你要注意锁里面办完事了才设置inited为true,这是很常见的优化手法,保证正确性的同时优化性能,后续调用这个方法都不必加解锁
public synchronized void register(int identityNumber, AbstractValueDecoder decoder) { decoderMap.put(identityNumber, decoder); inited = true; // initDefaultDecoder没执行完就改写了true啊 } public synchronized void clear() { decoderMap.clear(); inited = true; }
public void initDefaultDecoder() {
//多个线程可以同时进入该方法
if (inited) {
return;
}
synchronized (this) {
if (inited) {
return;
}
register(SerialPolicy.IDENTITY_NUMBER_JAVA, defaultJavaValueDecoder());
// A线程执行到了这一刻,inited已经改写成true了,B线程发现是true就返回了,但是后面的KryoValueDecoder,Kryo5ValueDecoder还没有注册进去啊
register(SerialPolicy.IDENTITY_NUMBER_KRYO4, KryoValueDecoder.INSTANCE);
register(SerialPolicy.IDENTITY_NUMBER_KRYO5, Kryo5ValueDecoder.INSTANCE);
// register(SerialPolicy.IDENTITY_NUMBER_FASTJSON2, Fastjson2ValueDecoder.INSTANCE);
inited = true; //这里好像没用啊,上面已经设置true了
}
}
DecoderMap类initDefaultDecoder方法没有执行完初始化的时候,调用register就把inited设置成了true(### 这里说的是initDefaultDecoder方法调用了register不是说用户自己调用****),导致没有初始化完就有其他线程使用报错(A线程调用initDefaultDecoder方法执行到register(SerialPolicy.IDENTITY_NUMBER_JAVA, defaultJavaValueDecoder())的时候,把inited改成了true,此时register(SerialPolicy.IDENTITY_NUMBER_KRYO5, Kryo5ValueDecoder.INSTANCE还没有执行完毕,没有完全初始化;此刻B线程在A进入inited判断为true直接返回,导致只初始化一个defaultJavaValueDecoder的时候返回)
我知道了,问题是在于register方法
cause by java.lang.NullPointerException: no decoder for identity number:-153049664 项目刚启动时有并发读redis缓存会报错,因为DecoderMap类initDefaultDecoder方法没有执行完初始化的时候,调用register就把inited设置成了true,导致没有初始化完就有其他线程使用报错