java arraylist通过java 8中的remove方法返回相同的对象

时间:2016-08-04 12:09:16

标签: java multithreading arraylist synchronization

我有两个共享相同列表的线程,第一个线程在列表中添加新对象,第二个是在同一个列表上执行删除。经过一段时间的开始,两个线程我都有相同的对象,为什么以及如何?

JAVA代码: SubmitJob将ThreadRuning类添加到列表中。

public class SubmitJob extends Thread{

    public SubmitJob(List l){
        list = l;
    }
    List<ThreadRuning> list;
    private static int counter =0;
    public void run(){

        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ThreadRuning t = new ThreadRuning();
            t.setThreadname("Thread Name "+counter);
            t.setStarttime(new Date().getTime());
            list.add(t);
            //System.out.println("Submited"+t.getThreadname());
            counter++;
        }
    }
}

第二类只是从列表中删除对象。

public class JobMoniter extends Thread {

    public JobMoniter(List l){
        list = l;
    }

    List<ThreadRuning> list;

    public void run() {
        while (true) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            if (list.size()>0) {
                ThreadRuning t = list.remove(0);
                System.out.println(t.getState()+" name "+t.getThreadname());
                if(t!=null)t.start();
                //System.out.println("Startinf job"+t.getThreadname());
            }
        }
    }
}

当我得到错误java.lang.IllegalThreadStateException我认为这是相关的线程问题,经过一些研究后我发现因为尝试两次启动相同的线程。并且得到这个错误所以我提示我从列表中得到的线程的名称比我知道remove方法在列表大小增加后返回相同的对象两次,在java中是否可能? 其余代码如下。

public class ThreadUtile {

    public static void main(String[] args) {
        List<ThreadRuning> list = new ArrayList<ThreadRuning>();
        new SubmitJob(list).start();
        new JobMoniter(list).start();

    }

}

ThreadRuning上课:

public class ThreadRuning extends Thread{

    private long starttime;
    private String threadname;

    public void run(){
        for(int i=0;i<10;i++){
            //System.out.println("Name of Thread "+threadname+ "Executing times"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
// getter setter.


}

注意:我谷歌但不是为什么? 如果我从t.start()类中删除JobMoniter部分并将线程名称存储在字符串中,并在下次多次找到相同名称时进行比较,但不是每次都进行比较。

对于任何进一步的查询,请在评论中写下。

2 个答案:

答案 0 :(得分:2)

除了您尝试在ArrayList上的并发环境中工作之外,您应该真正了解Java中的引用。

if (list.size()>0) {
    ThreadRuning t = list.remove(0);
    System.out.println(t.getState()+" name "+t.getThreadname());
    if(t!=null)t.start();
    //System.out.println("Startinf job"+t.getThreadname());
}

方法ArrayList.remove(int index)返回已删除的对象。它不会创建它的新实例。这就是当你再次尝试启动线程时获得IllegalThreadStateException的原因。

答案 1 :(得分:1)

当您共享一个对象时,它需要是线程安全的,以避免不可预测的行为导致难以修复的错误。在这里您的共享ArrayList不是线程安全的,您应该使用线程安全集合。如果您确实需要List,只需使用装饰器Collections.synchronizedList(List)作为下一个:

List<ThreadRuning> list = Collections.synchronizedList(new ArrayList<ThreadRuning>());
new SubmitJob(list).start();
new JobMoniter(list).start();

然而,Queue似乎更合适,ConcurrentLinkedQueue(本地线程安全的Queue实现)可能是一个不错的选择。