700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > ArrayMap java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]

ArrayMap java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]

时间:2021-02-02 05:39:00

相关推荐

ArrayMap  java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]

错误堆栈:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]at android.support.v4.util.SimpleArrayMap.allocArrays(SourceFile:183)at android.support.v4.util.SimpleArrayMap.put(SourceFile:437)

错误原因:

由于SimpleArrayMap 里面使用了一个静态变量的缓存,mBaseCache,

static Object[] mBaseCache;

该变量默认有两个数据,第1个元素是一个object[],用于存放上次的缓存的mBaseCache

第二个元素是int[],用于存在hash。具体赋值代码可以看下面

synchronized (ArrayMap.class) {if (mBaseCacheSize < CACHE_SIZE) {array[0] = mBaseCache;array[1] = hashes;for (int i=(size<<1)-1; i>=2; i--) {array[i] = null;}mBaseCache = array;mBaseCacheSize++;if (DEBUG) Log.d(TAG, "Storing 1x cache " + array+ " now have " + mBaseCacheSize + " entries");}}

使用该数组的地方在:

SimpleArrayMap 的allocArrays 方法里

synchronized (ArrayMap.class) {if (mBaseCache != null) {final Object[] array = mBaseCache;mArray = array;mBaseCache = (Object[])array[0];mHashes = (int[])array[1];array[0] = array[1] = null;mBaseCacheSize--;if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes+ " now have " + mBaseCacheSize + " entries");return;}}

下面这段代码是有风险的,如果mBaseCache 在多线程被修改了,就会抛ClassCastException 异常。

mBaseCache = (Object[])array[0];

解决方法:

如果项目某个地方报这个错误,请把这个地方的ArrayMap替换成 ConcurrentHashMap. 不需要把项目中所有的地方都替换掉,没有必要。单独线程,ArrayMap 完全没有问题。

错误复现

这个复现起来超级麻烦,我花了一周的时间,才找到复现的漏洞,分享给大家:

/*** 复现该问题 用了四个线程*java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]* at android.support.v4.util.SimpleArrayMap.allocArrays(SimpleArrayMap.java:157)* at android.support.v4.util.SimpleArrayMap.put(SimpleArrayMap.java:399)* at com.example.fragment.MainFragment$14.run(MainFragment.java:280)* 1.首先 线程1 执行到put 方法的* mArray[index<<1] = key;* mArray[(index<<1)+1] = value;* mSize++;* return null;* 最上面这个位置 目的是让这个数组不再是空的** 2.执行线程2 也执行到* mArray[index<<1] = key;* mArray[(index<<1)+1] = value;* mSize++;* return null;* 最上面这个位置 目的是让这个put 的东西,放在第0个位置,因为put里面会生成index,* 让两个线程都放到index 是0 的位置** 3.把线程1执行完,这样数据里面已经放进去一个数据了** 4.执行线程3 到removeAt 方法的 freeArrays 的 mBaseCache = array; 之前* public V removeAt(int index) {* final Object old = mArray[(index << 1) + 1];* if (mSize <= 1) {* // Now empty.* if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");* freeArrays(mHashes, mArray, mSize);** mBaseCache = array;----------- freeArrays** 这个的目的是调用freeArray 方法,让当前的map释放当前的数组。这样就可以生成mBaseCache了** 5.把线程2 执行完* 这样就会把mBaseCache 赋值的数组,重新赋值** 6.把线程3执行完* ok,现在mBaseCache已经被污染了** 7.执行线程4**/private void CMETestCastException() {final ArrayMap testArrayMap = new ArrayMap();final ArrayMap testArrayMap2 = new ArrayMap();new Thread("线程1"){@Overridepublic void run() {super.run();testArrayMap.put("2324","fffff");}}.start();new Thread("线程2"){@Overridepublic void run() {super.run();testArrayMap.put("test","string");}}.start();new Thread("线程3"){@Overridepublic void run() {super.run();testArrayMap.removeAt(0);}}.start();new Thread("线程4"){@Overridepublic void run() {super.run();testArrayMap2.put("aaa","string");}}.start();}

复现这个问题的时候,关键是把mBaseCache 污染掉。这里四个线程的话,需要调试,调试步骤就是上面我注释的。

总结:

如果当前的map 会有多个线程访问,请使用HasMap. 该问题,google 并没有解决。在高版本上,直接扔CME (ConcurrentModificationException.)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。