簡體   English   中英

同步方法中的Java線程安全錯誤

[英]java thread safety error in synchronized method

我有這堂課

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 BUG! MORE: 12 max: 12這是因為我在getBitmap()方法中再次檢查了值。 為什么?

我不明白,如果動畫是synchronized ,moveCount怎么會大於11?

如果模擬器滯后,則這種情況會更頻繁地發生。

您將在已synchronized塊中遞增和重置moveCount ,但是當您在getBitmap()方法中訪問moveCount變量時,您並未在同一鎖上進行同步。

這意味着線程A可以在animate()方法的中間,並且將moveCount遞增為與moveCountMax相等。 然后,線程B進入getBitmap()並讀取moveCount的值, 然后線程A將moveCount重置為0。

通常,不僅需要在寫入變量的值時同步,還需要在讀取變量時(在同一鎖上)進行同步,尤其是在對該變量進行的一項操作(例如animate()方法)時,尤其要進行同步。涉及復合操作(遞增,然后可能重置為0)

順便說一句,如果moveCountMax是一個常量值( = moves.length ),則將其標記為final

您需要同步對共享可變數據的所有訪問。 您可以在增量上進行同步,這很好,但是您不能在getBitmap的讀取上進行同步。 這意味着線程可以在遞增或緊隨其后讀取getBitmap moveCount

假設您增加了moveCount並且在該遞增線程將其設置為0之前,另一個線程調用了getBitmap,其中if (moveCount >= moveCountMax) {可以為true。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM