Java - 两个线程不同步

时间:2014-12-03 00:30:36

标签: java multithreading mouse mouselistener

我正在编写一个简单的Top Down Shooter。玩家可以按住鼠标连续拍摄。由于没有本机方法来检查鼠标按钮是否按下,我实现了这个解决方案:https://stackoverflow.com/a/6828990/3716866

这完全没问题。但问题是我将每个子弹对象和每个GameObject(敌人,玩家,桶等)放在一个链接列表中(两个列表一个用于对象,一个用于子弹)。当你射击子弹时,它会被添加到LinkedList中。游戏循环的每个runthrough整个List都会通过碰撞检查。在射击大约2次射击后,游戏因Null指针异常而崩溃。我认为问题是,因为我使用第二个线程来实现鼠标按钮按住,一个子弹对象正在创建,而在另一个线程中它检查列表。由于我正在检查列表,当大小被改变时,它正在检查列表,它崩溃了。但那只是我的理论。有人知道如何保持同步吗?

如果您需要更多代码等,请告诉我,我会上传它。我只是不确定我应该发布什么。所以在我发送垃圾邮件之前,我想等待你的反馈。

例外:

Exception in thread "Thread-2" java.lang.NullPointerException
    at shooter.main.Handler.checkForCollision(Handler.java:50)
    at shooter.main.Handler.tick(Handler.java:41)
    at shooter.main.Game.tick(Game.java:189)
    at shooter.main.Game.run(Game.java:290)
    at java.lang.Thread.run(Unknown Source)

Handler Class,它运行每个Object的所有tick和update方法,包括碰撞:

http://pastebin.com/mb206jug

我的MouseListener类,其中实现了另一个Thread:

http://pastebin.com/LfGWa5se

我的主要游戏课程,虽然我不认为这对这个问题非常重要:

http://pastebin.com/kW3UmZuD

我的抽象GameObject类:

http://pastebin.com/HbsdCUch

GameObject示例:

http://pastebin.com/vXCWZwtf

我的抽象弹药课程:

http://pastebin.com/dBnyXwgM

子弹的例子:

http://pastebin.com/XapmnsBv

拍摄方法如何:

public void shoot() {
    //play sound effect
    // fire bullet
    game.getHandler().addBullet(new Bullet9mm(game.getHandler().getPlayer().getX(), game.getHandler().getPlayer().getY(), game.getBulletImageManager(), game));
    // remove 1 bullet from magazine                    
    game.getHandler().getPlayer().getCurWeapon().setMagAmmo(-1);
}

我希望我提供了所有必要的信息。如果您还需要我的代码中的其他内容,请告诉我。

2 个答案:

答案 0 :(得分:1)

LinkedList删除任何后续元素向左移动(从索引中减去一个)。返回从列表中删除的元素。因此,如果你正在删除它可能没有你访问的子弹或播放器。你可以突出你的课程和方法签名,我自己或有人可以帮助你更好地设计它。

答案 1 :(得分:1)

即使没有多线程访问,您的实现也会失败,因为外部for循环没有考虑到您可能在迭代时删除对象。因此,即使由于冲突而移除了对象,计数器s也总是递增。出于这个原因,在您的实现中跳过了被删除的对象的每个后继者。

(说明:如果删除位置s处的对象,则所有连续对象都会向前移动一个位置,因此位置s + 1处的对象移动到位置s。但是作为for循环之后增加s,跳过该位置的新对象。

对于带有计数器的循环,你应该总是使用迭代器。它们提供安全的对象删除,并且在您想要更改集合的实现时更灵活。 (在您的情况下,迭代器提供的性能远远优于计数器,因为LinkedList上基于索引的操作需要顺序遍历列表。)

这是一个示例实现,有一些更多改进,应该适用于您的情况:

private void checkForCollision() {
    // use iterator instead of index based for loop
    while (Iterator<GameObject> iter = objects.iterator(); iter.hasNext();) {
        GameObject object = iter.next();
        // in case the object is the player, we can skip all collision tests
        if (object == getPlayer()) continue;

        // use implicit iterator instead of index based for loop
        for (Bullet bullet : bullets) {
            if (object.getCol().getBounds2D().intersects(bullet.getCol().getBounds2D())) {
                iter.remove(); // remove object via iterator
                break; // no need to check the rest => leave inner loop
            }
        }
    }
}

注意:此实现线程安全,因此,如果您的对象(集合objectsbullets或其中的任何对象)被访问通过多个线程,需要同步或线程安全的集合!