Runnable 中抛出java.lang.ArrayIndexOutOfBoundsException: -1

[已解决] SimpleDateFormat.format并发调用会有问题private SimpleDateFormat.StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) 里面用了calendar.setTime(date);这玩意是全局的,后面还会调用它,所以并发时候会出问题。
Random中protected Random.next(int bits);采用AtomicLong的CAS操作并发时候不会出问题。


每个Task有自己的ArrayList实例,在多线程情况下每个ArrayList实例应该不存在并发写入啊,为没什么还报ArrayIndexOutOfBoundsException: -1
求助大神,网上找了好久没结果。


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.lang.System.out;

public class TestArrayList {

    public static void main(String[] args){
        TestArrayList t= new TestArrayList();
        t.testInsertRows();
    }

    private static int millisecondOf1Day= 1000*3600*24;
    private static int millisecondOf5Min= 1000*60*5;
    private static final SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private final ExecutorService pool= Executors.newFixedThreadPool(8);

    public void testInsertRows(){
        Random random= new Random();
        int taskId= 0;
        for(int i=0; i< 8; i++){
            taskId++;
            pool.submit(new Task(taskId, 10000, null));
        }
        out.println("all submited!");
    }

    static class Task implements Runnable{
        private Integer id;
        private List<Bean> list;
        private int rowCount;
        private int count=0;
        private Random random;

        public Task(Integer id, Integer rowCount, Random random){
            this.id= id;
            this.list= new ArrayList<>(rowCount+1);
            this.rowCount= rowCount;
//          this.random= random;
            this.random= new Random();
        }

        @Override
        public void run() {
            out.println("task:"+id+" is running! "+Thread.currentThread());
            try{
                for(int j=0; j< rowCount; j++ ){
                    Long randomTime= random.nextLong();
                    Date validityDate= new Date(randomTime- millisecondOf1Day),
                            startDate= new Date(randomTime),
                            endDate= new Date(randomTime+ 2*millisecondOf1Day);
                    Bean bean = new Bean();
                    bean.setField0("K0000033");
                    bean.setField1("0001");
                    bean.setField2(sdf.format(validityDate));
                    bean.setField3(new Double(0.0).toString());
                    bean.setField4(sdf.format(startDate));
                    bean.setField5(sdf.format(endDate));
                    list.add(bean);
                }
                out.println("list is prepared in thread:"+id);
                Thread.sleep(millisecondOf5Min);//
                out.println(count+" rows has been inserted in thread:"+id); 
            }catch (Exception e) {
                synchronized (out) {                    
                    out.println("task:"+id+" in error"+Thread.currentThread());
                    e.printStackTrace();
                    out.println("task:"+id+" error info print completed");
                }
            }
        }
    }

}

class Bean{
    private String field0;
    private String field1;
    private String field2;
    private String field3;
    private String field4;
    private String field5;
    private String field6;
    /**
     * @return the field0
     */
    public String getField0() {
        return field0;
    }
    /**
     * @param field0 the field0 to set
     */
    public void setField0(String field0) {
        this.field0 = field0;
    }
    /**
     * @return the field1
     */
    public String getField1() {
        return field1;
    }
    /**
     * @param field1 the field1 to set
     */
    public void setField1(String field1) {
        this.field1 = field1;
    }
    /**
     * @return the field2
     */
    public String getField2() {
        return field2;
    }
    /**
     * @param field2 the field2 to set
     */
    public void setField2(String field2) {
        this.field2 = field2;
    }
    /**
     * @return the field3
     */
    public String getField3() {
        return field3;
    }
    /**
     * @param field3 the field3 to set
     */
    public void setField3(String field3) {
        this.field3 = field3;
    }
    /**
     * @return the field4
     */
    public String getField4() {
        return field4;
    }
    /**
     * @param field4 the field4 to set
     */
    public void setField4(String field4) {
        this.field4 = field4;
    }
    /**
     * @return the field5
     */
    public String getField5() {
        return field5;
    }
    /**
     * @param field5 the field5 to set
     */
    public void setField5(String field5) {
        this.field5 = field5;
    }
    /**
     * @return the field6
     */
    public String getField6() {
        return field6;
    }
    /**
     * @param field6 the field6 to set
     */
    public void setField6(String field6) {
        this.field6 = field6;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Bean [field0=" + field0 + ", field1=" + field1 + ", field2=" + field2 + ", field3=" + field3
                + ", field4=" + field4 + ", field5=" + field5 + ", field6=" + field6 + "]";
    }
}

task:1 is running! Thread[pool-1-thread-1,5,main]
task:4 is running! Thread[pool-1-thread-4,5,main]
task:3 is running! Thread[pool-1-thread-3,5,main]
task:2 is running! Thread[pool-1-thread-2,5,main]
task:5 is running! Thread[pool-1-thread-5,5,main]
task:6 is running! Thread[pool-1-thread-6,5,main]
all submited!
task:7 is running! Thread[pool-1-thread-7,5,main]
task:8 is running! Thread[pool-1-thread-8,5,main]
task:7 in errorThread[pool-1-thread-7,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:7 error info print completed
task:8 in errorThread[pool-1-thread-8,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:65)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:8 error info print completed
task:4 in errorThread[pool-1-thread-4,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:62)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:4 error info print completed
task:1 in errorThread[pool-1-thread-1,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:1 error info print completed
task:5 in errorThread[pool-1-thread-5,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:65)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:5 error info print completed
task:3 in errorThread[pool-1-thread-3,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:3 error info print completed
task:6 in errorThread[pool-1-thread-6,5,main]
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Calendar.getDisplayName(Unknown Source)
at java.text.SimpleDateFormat.subFormat(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.SimpleDateFormat.format(Unknown Source)
at java.text.DateFormat.format(Unknown Source)
at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
task:6 error info print completed
list is prepared in thread:2

yhk1996
needRestart protected Random.next(int bits);采用AtomicLong的CAS操作
6 个月之前 回复
yhk1996
needRestart private SimpleDateFormat.StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) 里面用了calendar.setTime(date);这玩意是全局的估计在这就出毛病了
6 个月之前 回复

2个回答

Random 不要公用,改为线程内新建再试试

yhk1996
needRestart 了解了,之前是看别人也用单例才把random用单例的。原来这个采纳后就不能修改了……
6 个月之前 回复
weixin_39606236
꧁gaoKuo꧂ 回复GaoKuoZ: 看了下文档,我Random也理解错了 java.util.Random的java.util.Random是线程安全的。 但是,跨线程的同时使用java.util.Random实例可能会遇到争用,从而导致性能下降。
6 个月之前 回复
weixin_39606236
꧁gaoKuo꧂ 回复needRestart: 我都没看到sdf.... Random也不要线程共享哦
6 个月之前 回复
yhk1996
needRestart 感谢大佬提醒
6 个月之前 回复
yhk1996
needRestart 破案了……是simpleDateFormat被多线程调用出的错,Random不会出并发错误。之前报错的行数是乱的也一直没注意这个。
6 个月之前 回复
yhk1996
needRestart 是结束
6 个月之前 回复
yhk1996
needRestart 我把cache打印时候加锁了,保证出错时候只有一个线程打印输出,"task:n in error…"是开始,"task:n error info…
6 个月之前 回复
weixin_39606236
꧁gaoKuo꧂ 回复needRestart: 错误信息发一下 全部的
6 个月之前 回复
yhk1996
needRestart 刚试了还是一样的……吐血啊,上Google也搜了,就是没找到相关的。
6 个月之前 回复

线程中用了那么多的成员变量,这种很容易出现线程数据的问题,就好比卖火车票一样,要考虑线程的安全性的

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!