同步方法中的java线程安全错误

时间:2012-09-13 20:06:13

标签: java android multithreading thread-safety

我有这个班级

package net.omnosis.mazegame.components;

import net.omnosis.mazegame.SlicedBitmap;
import android.graphics.Bitmap;

public class PlayerLayer extends DrawableLayer {

    private Player player;
    private XY tileImageSize;

    private int[] move = new int[] { 1, 2, 3, 4, 3, 2, 1, 6, 7, 8, 7, 6 };
    //private int[] move = new int[] { 8 };

    private int moveCount;
    private int moveCountMax = move.length;

    private Bitmap playerBitmap;
    public SlicedBitmap playerTiles;

    private int line;

    private static final int VERTICAL = 0;
    private static final int HORIZONTAL = 8;

    public PlayerLayer(Player player, Bitmap playerBitmap, XY tileImageSize) {

        this.playerBitmap = playerBitmap;
        this.tileImageSize = tileImageSize;
        playerTiles = new SlicedBitmap(playerBitmap, tileImageSize.x(), tileImageSize.y());

        setPlayer(player);

        update();
    }

    public final void setPlayer(Player player) {
        if (this.player != null) {
            this.player.removeListener(this);
        }

        this.player = player;
        player.addListener(this);
        update();
    }

    public void updateDirection() {
        Direction dir = player.getHeading();

        if (dir == Direction.LEFT || dir == Direction.RIGHT) {
            line = HORIZONTAL;
        } else if (dir == Direction.TOP || dir == Direction.BOTTOM) {
            line = VERTICAL;
        }
    }

    public synchronized void animate() {

        if (player.isMoving()) {

            moveCount++;

            if (moveCount >= moveCountMax) {

                player.finishMove();
                moveCount = 0;
            }
        } else {

        }

        updateDirection();
        super.update();
    }

    public void update() {
        updateDirection();
        super.update();
    }

    public XY getSpritePos() {
        XY playerPos = new XY(player.getCurrentPosition().x() * tileImageSize.x() + (tileImageSize.x() / 2), player.getCurrentPosition().y() * tileImageSize.y() + (tileImageSize.y() / 2));
        XY animationPos = getAnimationPos();
        return playerPos.add(animationPos);
    }

    public XY getAnimationPos() {
        double step = (double) tileImageSize.x() / moveCountMax * moveCount;
        return player.getHeading().multiply((int) step);
    }

    public Bitmap getBitmap() {

        if (moveCount >= moveCountMax) {
            System.out.println("BUG! MORE: " + moveCount + "  max: " + moveCountMax);
            moveCount = 0;
        }
        return playerTiles.getTile(move[moveCount] + line);
    }
}

一个线程每10毫秒调用animate方法。有时我得到这个输出:BUG! MORE: 12 max: 12这是因为我在getBitmap()方法中检查了值AGAIN。为什么呢?

我不明白,如果动画是synchronized,那么moveCount如何超过11?

如果模拟器滞后,则会更频繁地发生这种情况。

2 个答案:

答案 0 :(得分:3)

您正在moveCount块中递增并重置synchronized,但在moveCount方法中访问getBitmap()变量时,您没有在同一个锁上进行同步。

这意味着线程A可以位于animate()方法的中间位置,并且已增加moveCount以等于moveCountMax。线程B然后输入getBitmap()并在线程A将moveCount重置为0之前读取moveCount 的值。

通常,您不仅需要在写入变量值时进行同步,还需要在从中读取变量时(在同一个锁上)进行同步,尤其是对于该变量的某个操作(例如{{ 1}}方法)涉及复合操作(增量,然后可能重置为0)

顺便说一句,如果animate()是一个常数值(moveCountMax),请将其标记为= moves.length

答案 1 :(得分:1)

您需要同步对共享可变数据的所有访问权限。您可以对增量进行同步,这很好,但是您不会在getBitmap中进行同步。这意味着线程可以在moveCount中读取getBitmap,同时增加或紧接在后。

想象一下你增加moveCount的情况,在增量线程将其设置为0之前,另一个线程调用getBitmap,其中if (moveCount >= moveCountMax) {在那一刻可以为真。