0

私はこのクラスを持っています

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);
    }
}

スレッドはanimate10ミリ秒ごとにメソッドを呼び出します。時々この出力が得られます:BUG! MORE: 12 max: 12これは、getBitmap()メソッドで値AGAINをチェックするためです。なんで?

synchronizedアニメーションがである場合、moveCountが11を超える可能性があることを理解していません。

エミュレータが遅れると、これはより頻繁に発生します。

4

2 に答える 2

3

moveCountブロック内でインクリメントとリセットを行っていますが、メソッドで変数にsynchronizedアクセスするときに同じロックで同期していません。moveCountgetBitmap()

これは、スレッドAがメソッドの途中にある可能性があり、に等しくなるようにanimate()インクリメントされたことを意味します。次に、スレッドBは、スレッドAが0にリセットされる前に、の値を入力して読み取ります。moveCountmoveCountMaxgetBitmap()moveCount moveCount

一般に、変数の値に書き込むときだけでなく、(同じロックで)変数から読み取るときにも同期する必要があります。特に、その変数の操作の1つ(animate()メソッドなど)に複合操作が含まれる場合はそうです。 (インクリメントしてから、おそらく0にリセットします)

ところで、moveCountMaxが定数値(= moves.length)の場合は、としてマークしfinalます。

于 2012-09-13T20:10:44.720 に答える
1

共有された可変データへのすべてのアクセスを同期する必要があります。増分で同期します。これは良いことですが、読み込みでは同期しませんgetBitmap。これは、スレッドがインクリメント中または直後に読み込むことができることを意味しmoveCountますgetBitmap

moveCountインクリメントし、そのインクリメントスレッドがそれを0に設定する前に、別のスレッドがgetBitmapを呼び出す場合を想像してください。これif (moveCount >= moveCountMax) {は、その時点でtrueになる可能性があります。

于 2012-09-13T20:12:21.383 に答える