2

JPanel の境界から外れた場合、リストから発射物を削除するためにイテレータを使用しています。イテレータを使用する前は機能しませんでしたが、イテレータを使用すると、メソッドを a の try-catch に入れる限り機能しConcurrentModificationExceptionます。コードは機能し、リストから発射物を正常に削除しますが、約 30% の確率でキャッチがヒットし、プログラムでスタッターが発生します。なぜ散発的にしか検出されないのかはわかりませんが、限られた時間でそれを見ることができた私の教授は、同期の問題である可能性があると考えました.

私のプログラムを実行しているループは次のとおりです。

private void executeGameLoop() 
        {

            long nextFrameStart = System.nanoTime();
            while(panel.getRunning()) 
            {
                do 
                {
                    panel.repaint();
                    nextFrameStart += FRAME_PERIOD;
                } while(nextFrameStart < System.nanoTime());

                long remaining = nextFrameStart - System.nanoTime();
                panel.update();

                if (remaining > 0) 
                {
                    try 
                    {
                        Thread.sleep(remaining / 1000000);
                    } 
                    catch(Throwable e) 
                    {
                        System.out.println(e.getMessage()); 
                    }
                }
            }
        }

これはループ at によって呼び出されますpanel.update。今のところ、発射体の更新を処理します。

public void update()
        {       
            randX = (int)((Math.random() * getWidth()) - (int)(Math.random() * getWidth()));
            randY = (int)((Math.random() * getHeight()) - (int)(Math.random() * getHeight()));

            int sizeX;
            int sizeY;

            try
            {
                Iterator<Projectile> it = shots.iterator();
                for(Projectile a : shots)
                {
                    if(!a.equals(null))
                    {   
                        sizeX = a.getDisplayX();
                        sizeY = a.getDisplayX();

                        if((!checkCoords((int)a.getX(), (int)a.getY(), sizeX, sizeY)) && a.hasTarget())
                        {
                            a = null;
                            if(it.next().equals(null));
                                it.remove();
                        }

                        else if(a.hasTarget())
                        {
                            a.update();
                        }
                    }   
                }
            }
            catch (ConcurrentModificationException e){ System.out.println(e.getMessage() + " Catch"); } 
        }

これらの最後の 2 つの方法は、発射物を作成するための私のメカニズムです。

private void createProjectile(int x, int y)
{
    total++;
    if(shots.size() < shotCount)
    {
        Projectile temp = new Projectile(randX, randY);
        temp.setTarget((x + temp.getSprite().getDisplayImg().getWidth() / 8),
                        (y - temp.getSprite().getDisplayImg().getHeight() / 8));
        temp.setHasTarget(true);
        shots.add(temp);
        msg("Target: (" + x + ", " + y + ")");
    }
}

@Override
public void mouseClicked(MouseEvent e) 
{
    createProjectile(e.getX(), e.getY());
}

なぜこれが起こっているのか、またはそれを修正する方法についての洞察をいただければ幸いです。

4

2 に答える 2

6

ループに openIteratorがあり (さらに余分な)、に値を追加しています。で両方のブロックを作成するか、(私の推奨) のコピーを作成して次の図面を作成する必要があります。forIterator itcreateProjectilesynchronizedshotsshots

List<Projectile> shotsToPaint;
synchronized(shots) { shotsToPaint = [`ImmutableList`][1].copyOf(shots); }

で適切な同期を適用しますcreateProjectile。パフォーマンスの影響度に応じて、メソッド全体を で同期するか、 で同shots期しshots、サイズを確認し、非同期ブロックに新しいものを作成してProjectileから、同期してリストのサイズを再確認して追加することができます。

于 2013-09-11T22:35:14.063 に答える
3

for ループは問題の 1 つです。次のようなループを作成すると:

for (Projectile a : shots) {...}

コンパイラは暗黙的に次のように変換します。

for (Iterator<Projectile> i = shots.iterator; i.hasNext();) {
    Projectile a = i.next();
    ...
} 

したがって、合計で 2 つのイテレータがあります。1 つ目は明示的に を呼び出したときに作成されshots.iterator()、2 つ目は for ループでコンパイラによって暗黙的に作成されます。

原因の 1 つConcurrentModificationExceptionは、リストを繰り返し処理しているときに、他の誰かがリストを変更した場合です。通常、「誰か他の人」は別のスレッドにいるため、教授は同期の問題を疑っていますが、この場合、「誰か他の人」は他の iteratorです。

編集:クリリスが指摘したように、別のスレッドからの干渉もあります。

于 2013-09-11T22:35:52.693 に答える