1. ArrayList是非线程安全的,程序将会去读配置文件并将获取的信息放置到一个静态ArrayList中。如果这个ArrayList不为空,则会直接返回。现在有一个线程在ArrayList为空的时候进入了读取配置的操作中并不停的向ArrayList中添加数据,而另外一个线程则判断ArrayList已经不为空,则直接返回了。之后这个ArrayList会做为
参数来new一个新的ArrayList,这时候错误发生了。
我们可以看:
public ArrayList(Collection<? extends E> c) {
size = c.size();
// Allow 10% room for growth
int capacity = (int) Math.min((size*110L)/100, Integer.MAX_VALUE);
elementData = (E[]) c.toArray(new Object[capacity]);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
其实在toArray(T[] a)方法中已经为了保险起见,判断了数组的长度,如果小的话,会重建一个新的数组。However,实际上就在执行copy方法之前,size还是由于另外一个线程还在添加新的信息又发生了改变,所以ArrayIndexOutOfBoundsException还是被抛出了。
在使用这非线程安全的容器是还是应该多长几个心眼的,这样能尽量避免这些问题的出现。
2. 这是一个关于WebsphereMQ 的 messageID的问题,我们的Exception处理中包含这么一个逻辑,一个被定义为Level A的Exception,抛出后会把消息backout回MQ里以便可以后续处理,5次重试后,就会变成Level B的Exception并将信息持久化到数据库里,并把消息继续backout,但是会把这个消息的messageID存入到HashMap里面。读消息的时候会判断这个HashMap,如果这个messageID被存入了,则不会做任务处理,把MQ commit掉。
然而诡异的事情发生了,不该commit的消息commit掉了,但是没做任何正常处理,而出错的消息却又做了一遍,继续抛出level B的exception。
首先我们想到的是HashMap是一个非同步的对象,这样会出错吗?
我们仔细研究了JDK源代码,正如我们所理解的,被HashMap认为是相同的key必须满足hashcode和equals()方法一样,对于String对象而言,这个完全是不可能的。为了确信,这时候我们写了测试方法,启动了几个线程来向HashMap中不停的插入或删除键值,但是我们保证key值是都不一样的String,结果一切都很正常。
难道MessageID出现了重复,我又查看了IBM相关的资料,MessageID是按照一定的规则的产生,并且会保证唯一。当我们enqueue的时候保证messageID为空或者MQPutMessageOptions设置了选项MQC.MQPMO_NEW_MSG_ID的时候,WebsphereMQ理论上会给我们生成唯一的MessageID。
终于,谜底解开了,WebsphereMQ的JavaAPI给我们返回的是MessageID是byte数组,而代码直接使用new String(byte[])来转化成String。这样Java会采用系统默认的编码来转化。我的机器默认是GBK,而美国Soloris服务器则是使用美国本地的编码。就这样,我们可以看到,每个byte若为负,转化后到变成了63,也就是?号。所以出现了很多重复的ID,也就导致系统丢失该处理message。这时我们只需要采用ISO-8859-1来转成String就不会有任何的问题。
3.还有几个问题,基本上和1描述的差不多,都是因为多线程或是不同的服务器操作同样的资源而导致了BUG。
多线程一直会是一个困扰人的问题,编程的时候应该多考虑考虑,尽量避免出错。