Custom(de-)serialize方法抛出java.io.OptionalDataException

时间:2015-06-08 10:57:37

标签: java serialization

当我使用我的包装类序列化然后反序列化map.subMap时,我将遇到一个OptionalDataException。

似乎OptionalDataException与基元有关但在地图中我总是有对象框基元。那我怎么解决这个问题呢?

编辑:添加了同步块

public class SerializeableSubMap<K, V> implements NavigableMap<K, V>, Serializable {
    private static final long serialVersionUID = -7002458872266068959L;
    private NavigableMap<K, V> map;

    public SerializeableSubMap(NavigableMap<K, V> map) {
        this.map = map;
    }

private void writeObject(ObjectOutputStream stream) throws IOException {
    synchronized (map) {
        stream.writeInt(map.size());
        Iterator<Entry<K, V>> itr = map.entrySet().iterator();
        Entry<K, V> next;

        while (itr.hasNext()) {
            next = itr.next();
            stream.writeObject(next.getKey());
            stream.writeObject(next.getValue());
        }
    }
    //stream.close();
}

private void readObject(ObjectInputStream stream) throws IOException {
        int size = stream.readInt();
        map = new ConcurrentSkipListMap<K, V>();

        for(int i = 0; i < size; i++) {
            try {
                // get OptionalDataException here!
                map.put((K) stream.readObject(), (V) stream.readObject());
            } catch (Exception e) {
                throw new IOException(e);
            }
        }
    }

    // delegate map interface methods to map object
}

编辑2:

private void writeObject(ObjectOutputStream stream) throws IOException {
    synchronized (map) {
        int s =  map.size();

        stream.writeInt(map.size());
        Iterator<Entry<K, V>> itr = map.entrySet().iterator();
        Entry<K, V> next;

        int i=0;
        while (itr.hasNext()) {
            next = itr.next();
            stream.writeObject(next.getKey());
            stream.writeObject(next.getValue());
            i++;
        }

        if (s != i) {
            throw new ConcurrentModificationException();
        }
    }
    //stream.close();
}

1 个答案:

答案 0 :(得分:0)

我能想到的一个原因是对Map的并发修改。说出执行以下语句后删除时会发生什么?

stream.writeInt(map.size());

首先说当检索Map大小时,地图中存在的元素数量是2,但是说其他一些线程会同时删除地图中的一个元素,所以现在地图中存在的元素数量为1那么在流中写入的内容只有一个键值对(2个对象)。

但有趣的是,我们在读取大小中读到的是2个键值对(4个对象),因为我们将大小读为2,我们尝试使用2个键值对,即使写入的只是一个键 - 值对。

这是我们在该领域的许多客户所经历的事情。后来都被证明是一个并发的修改问题。所以我建议你检查一下。

entrySet的Javadoc不会确认任何修改都会引发并发修改异常。

  

Set> entrySet()

     

返回此映射中包含的映射的Set视图。该集由地图支持,因此对地图的更改将反映在集中,反之亦然。如果在对集合进行迭代时修改了映射(除非通过迭代器自己的删除操作,或者通过迭代器返回的映射条目上的setValue操作),迭代的结果是未定义的。该集支持元素删除,它通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作从地图中删除相应的映射。它不支持add或addAll操作。