Bug之java.io.OptionalDataException

产生背景:

在线上的生产环境中,登录时有一个将用户的访问权限写入redis缓存的逻辑,以便后面访问接口时候,快速验证用户是否具有权限。写入的时候,没有报错提示。但是取出来的时候,偶发性的会报Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.OptionalDataException。

分析:

写入是没有问题的,可以从redis里面看到确实写入的数据,但是数据是加密的,看不出来具体是什么。首先,还是看看java.io.OptionalDataException这个类的介绍。

/**
 * Exception indicating the failure of an object read operation due to
 * unread primitive data, or the end of data belonging to a serialized
 * object in the stream.  This exception may be thrown in two cases:
 *
 * <ul>
 *   <li>An attempt was made to read an object when the next element in the
 *       stream is primitive data.  In this case, the OptionalDataException's
 *       length field is set to the number of bytes of primitive data
 *       immediately readable from the stream, and the eof field is set to
 *       false.
 *
 *   <li>An attempt was made to read past the end of data consumable by a
 *       class-defined readObject or readExternal method.  In this case, the
 *       OptionalDataException's eof field is set to true, and the length field
 *       is set to 0.
 * </ul>
 *
 * @author  unascribed
 * @since   JDK1.1
 */

从类介绍看出的是,读取数据出现问题,有两个可能的原因,一个是读操作读取未读的原始数据,另一个是在流中的最后一个数据是序列化的对象。这两句话也看不出什么问题。网上查了一下有说要数据处理要同步。
无奈下,将存入的逻辑仔细debug好几十遍,出现这个错误的时候,存入的对象的序列化ID都是显示表明的,所以排除序列化ID不同造成的。最后,看存入的数据时,发现数据的数目总是变动,外部条件完全一致,获取的数据量总是不同,跟进去看到。使用了parallelStream,然后foreach,将元素加入到一个非线程安全的集合HashSet中,所以数据量一直变动。
为此我写了一段测试代码。

 public static void main(String[] args) throws InterruptedException {
        Collection<String> nameCollection = testParallelStream();
        System.out.println(nameCollection.size());
        nameCollection.forEach(name -> {
            System.out.println(name);
            System.out.println("sign");
        });
    }

    private static Collection<String> testParallelStream() {
        Set<String> nameSet = new HashSet<>();
        // Collection<String> collection = new HashSet<>();
        Collection<String> collection = Collections.synchronizedSet(new HashSet<>());
        for (int i = 0; i < 1000; i++) {
            nameSet.add("cj" + i);
        }
        nameSet.parallelStream().forEach(name -> {
            collection.add(name);
        });
        System.out.println(collection.size());
        return collection;
    }

这个里面如果不使用Collections.synchronizedSet(new HashSet<>()),则会造成线程安全问题。本质上parallelStream是多线程处理,而HashSet是个非线程安全的集合,往里面放数据的时候,会出现数据丢失。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值