暂停一个线程以防止ConcurrentModificationException?

时间:2012-01-12 02:26:08

标签: java multithreading

我正在创建一个Swing应用程序来制作游戏。它在屏幕外的随机位置创建图像,当它们离开屏幕时我想删除它们。请查看代码段:

public void checkTrolls(){ //CAUSES EXCEPTION ERROR WHEN SPRITE EXIT SCREEN
    for(AutomatedSprite a : trolls){
        if(a.getX() < 0 - a.getImage().getWidth())
            trolls.remove(a);
        if(a.getY() < 0 - a.getImage().getWidth())
            trolls.remove(a);
        if(a.getX() > 800)
            trolls.remove(a);
        if(a.getY() > 600)
            trolls.remove(a);
    }
}

@Override
public void run() {
    long beforeTime, timeDiff, sleep;

    beforeTime = System.currentTimeMillis();

    while(true){
        dodger.update(); //update sprite
        if(trolls.size() != 6){
            trolls.add(new AutomatedSprite("images/troll_face.png"));
        }
        for(Sprite troll : trolls){
            troll.update();         //UPDATES MY SPRITES
        }
        checkTrolls();             //CHECKS TROLLS EXITING THE SCREEN
        repaint();

        for(Sprite troll : trolls){
            System.out.println("X: " + troll.getX());
            System.out.println("Y: " + troll.getY());
        }

        timeDiff = System.currentTimeMillis() - beforeTime;
        sleep = timeDiff - DELAY;

        if(sleep < 0)
            sleep = 5;

        try {
            Thread.sleep(sleep);
        } catch (InterruptedException e) { e.printStackTrace(); }

        beforeTime = System.currentTimeMillis();
    }
}

巨魔是AutomatedSprites的Vector,当他们离开屏幕时我得到一个ConcurrentModificationException,显然我无法从我的向量中删除实例。

因此,当线程更新所有精灵时,我似乎无法从向量中删除任何内容,有没有办法暂停我的线程以便我可以删除精灵?

P.S:如果我错过了一些内容,这是整个班级:Pastebin

5 个答案:

答案 0 :(得分:5)

在迭代它时无法从集合中删除,对于单线程环境和多线程环境都是如此。 syncrhonized仍会导致问题,Thread.sleep也是如此。使用和迭代器并删除该方式。

public void checkTrolls(){
    for(Iterator<AutomatedSprite> itr = trolls.iterator(); itr.hasNext();){
        AutomatedSpring nextElemnt = itr.next();
        if(youShouldRemoveTheSprite){
            itr.remove();
        }
    }
}

所以在这里你使用你的巨魔集合提供的迭代器。而且你要求Iterator安全地从集合中删除对象。

现在,如果你执行了多个线程的checkTrolls,那么你需要进行同步。你可以这样做

public synchronized void checkTrolls(){ ...
根据您最近的评论/链接

修改

并不是将Iterator.next()赋值给变量,而是多次调用iterator.next()。每次调用next()时,都会将迭代器移动到List的下一个元素。因此,在一个循环迭代结束时,您将迭代器移动到列表中的第6个元素。如果你正在索引它,它看起来像:

   for(Iterator<AutomatedSprite> itr = trolls.iterator(); itr.hasNext(); ){
       if(trolls.get(0).getX() < 0 - trolls.get(1).getImage().getWidth() ||      
                      trolls.get(2).getY() < 0 - trolls.get(3).getImage().getWidth() ||
                       trolls.get(4).getX() > 800 ||
                       trolls.get(5).getY() > 600){
               trolls.remove(trolls.get(5));
       }

注意这个例子:在0,1,2,3,4 ...的索引仅用于演示,实际上它将是i = 0; start for loop trolls.get(i ++)。getX(),trolls.get(i ++)。getY()等等。如果您的列表是10,000,那么最终会得到NoSuchElementException

例如,如果你只有3个巨魔,一旦你到达第4个itr.next(),你将得到NoSuchElementException。因此,您需要将next()元素存储在变量中并处理该变量,以便itr.hasNext();正确返回,itr.remove()也能正常工作。

答案 1 :(得分:2)

checkTrolls()中,您在迭代它时从Vector删除元素。这将导致ConcurrentModificationException

来自Vector的javadoc

  

此类的迭代器和listIterator返回的迭代器   方法是快速失败的:如果向量在任何结构上被修改   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己删除或添加方法,迭代器会抛出一个   ConcurrentModificationException的。

几种选择:

创建一个元素列表,在完全迭代后删除和删除它们。

使用线程安全List实施,例如CopyOnWriteArrayList

答案 2 :(得分:0)

将您的代码放在synchronized块中的run方法中。 synchronized将确保一次只执行一个代码的一个线程。

run(){
synchronized(this){
....your code.
}}

您也可以run()同步。

答案 3 :(得分:0)

一个简单而有点天真的解决方案就是在trolls集合上进行简单的同步。如果这样做,请确保在Animator线程中yield为您的checkTrolls函数提供处理和清理的机会。

答案 4 :(得分:0)

我认为问题可能是在'checkTrolls'中你试图修改你正在迭代的向量。