无法在对象实例之间共享数据,但仅限于当前线程内

时间:2017-04-09 19:59:26

标签: java multithreading concurrency

现在我的Object类中有一个静态ArrayList,但是两个线程都在访问/更改数据到那个列表。我需要一个在Object的所有实例之间共享的ArrayList,但不需要在不同线程的Object实例之间共享。最好的方法是什么?以下是正在发生的事情的要点:

public class Game extends Thread {

    private Actions actions;
    private Player whitePlayer;
    private Player blackPlayer;
    private String gameID;
    private boolean gameOver;
    private int movesTaken;
    private Command currentCommand;
    private Player winner;

    public ArrayList<Settlement> SettlementList = new ArrayList<Settlement>();
    public ArrayList<Hex> SettledHexes = new ArrayList<Hex>();

    public Game(String gameID){
        whitePlayer = new Player(PlayerSide.WHITE);
        blackPlayer = new Player(PlayerSide.BLACK);
        actions = new Actions(true);
        actions.setCurrentPlayer(whitePlayer);
        this.gameID = gameID;
        gameOver = false;
        movesTaken = 0;

    }

    public void run(){
        while(!gameOver){

            if(commandIsInQueue()){

                Message currentMessage = GameData.getIncomingMessages().poll();

                if (currentMessage.getPlayerID().equals(whitePlayer.getPlayerID())) {
                    actions.setCurrentPlayer(whitePlayer);
                }
                else if(currentMessage.getPlayerID().equals(blackPlayer.getPlayerID())){
                    actions.setCurrentPlayer(blackPlayer);
                }
                else{
                    endGame();
                }
                System.out.println("Move made by: " + actions.getCurrentPlayer().getPlayerID() + " " + "in game: " + gameID);
                setCurrentCommand(currentMessage.getCommand());
                executeCommand();
                checkEndGameConditions();
                mergeAllSettlement();
                movesTaken++;
            }

            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void executeCommand(){

        int x, y, z, orientation;
        TerrainType hexATerrain, hexBTerrain, terrainTypeExpansion;

        switch(currentCommand.getCommandType()){

            case ROTATE_TILE : rotate();
                break;
            case PLACE_TILE :
                x = currentCommand.getX();
                y = currentCommand.getY();
                z = currentCommand.getZ();
                orientation = currentCommand.getOrientation();
                hexATerrain = currentCommand.getHexATerrain();
                hexBTerrain = currentCommand.getHexBTerrain();
                try{
                    placeTile(x, y, z, orientation, hexATerrain, hexBTerrain);
                }catch (InvalidMoveException e){
                    endGame();
                }
                break;
            case FOUND_SETTLEMENT :
                x = currentCommand.getX();
                y = currentCommand.getY();
                z = currentCommand.getZ();
                try {
                    foundSettlement(x, y, z);
                } catch (InvalidMoveException e) {
                    endGame();
                }
                break;
            case PLACE_TOTORO :
                x = currentCommand.getX();
                y = currentCommand.getY();
                z = currentCommand.getZ();
                try {
                    placeTotoro(x, y, z);
                } catch (InvalidMoveException e) {
                    endGame();
                }
                break;
            case PLACE_TIGER :
                x = currentCommand.getX();
                y = currentCommand.getY();
                z = currentCommand.getZ();
                try {
                    placeTiger(x, y, z);
                } catch (InvalidMoveException e) {
                    endGame();
                }
                break;
            case EXPAND_SETTLEMENT :
                x = currentCommand.getX();
                y = currentCommand.getY();
                z = currentCommand.getZ();
                terrainTypeExpansion = currentCommand.getTerrainTypeExpansion();
                try {
                    expandSettlement(x, y, z, terrainTypeExpansion);
                } catch (InvalidMoveException e) {
                    endGame();
                }
                break;
        }
    }

    private void checkEndGameConditions(){

        if(whitePlayer.hasNoPiecesLeft()){
            winner = whitePlayer;
            endGame();
        }
        else if(blackPlayer.hasNoPiecesLeft()){
            winner = blackPlayer;
            endGame();

        }
        else if(whitePlayer.hasOnlyOneTypeOfPieceLeft()){
            winner = whitePlayer;
            endGame();
        }
        else if(blackPlayer.hasOnlyOneTypeOfPieceLeft()){
            winner = blackPlayer;
            endGame();

        }
        else if(actions.getBoard().getNumberOfTilesOnBoard() > 48){
            calculateWinnerByScore();
            endGame();
        }
    }

    private boolean commandIsInQueue(){
        try {
            if (GameData.getIncomingMessages().peek().getGameID().equals(null)) {
                return false;
            } else if (GameData.getIncomingMessages().peek().getGameID().equals(gameID)) {
                return true;
            } else {
                return false;
            }
        }
        catch (Exception e) {System.out.println("Error in commandIsInQueue() method..");}
        return false;
    }

    public void setCurrentCommand(Command command){
        currentCommand = command;
        movesTaken++;
    }

    public   void endGame(){
        System.out.println("end of: " + gameID);
        gameOver = true;
        //SEND END GAME MESSAGE
    }

    protected  void forfeit(Player player){
        if(player.getPlayerSide() == PlayerSide.BLACK){
            winner = whitePlayer;
        }
        else{
            winner = blackPlayer;
        }
        //SEND FORFEIT MESSAGE
    }

    public   void calculateWinnerByScore(){
        if(whitePlayer.getScore() > blackPlayer.getScore()){
            winner = whitePlayer;
        }
        else if(blackPlayer.getScore() > whitePlayer.getScore()){
            winner = blackPlayer;
        }
        else{
            if(whitePlayer.getTotoroAvailable() > blackPlayer.getTotoroAvailable()){
                winner = blackPlayer;
            }
            else if(blackPlayer.getTotoroAvailable() > whitePlayer.getTotoroAvailable()){
                winner = whitePlayer;
            }
            else if(whitePlayer.getTigerAvailable() > blackPlayer.getTigerAvailable()){
                winner = blackPlayer;
            }
            else if(whitePlayer.getTigerAvailable() < blackPlayer.getTigerAvailable()){
                winner = whitePlayer;
            }
        }
    }

    public   static Location convertCoordinates(int x, int y, int z){
        return new Location(x + 100, z + 100);
    }

    public   void rotate(){
        actions.invertTile();
        actions.rotateTile();
        actions.rotateTile();

        if(actions.getCurrentTile().getOrientation() == 6)  actions.getCurrentTile().setOrientation(1);
        else{
            int temp = actions.getCurrentTile().getOrientation() + 1;
            actions.getCurrentTile().setOrientation(temp);
        }
    }

    public   void placeTile(int x, int y, int z, int orientation, TerrainType terrainTypeA, TerrainType terrainTypeB) throws InvalidMoveException {
        Location coord = null;
        switch(orientation) {
            case 1:
                actions.setCurrentTile(new Tile(new Hex(TerrainType.VOLCANO), new Hex(terrainTypeA), new Hex(terrainTypeB)));
                actions.invertTile();
                actions.getCurrentTile().setOrientation(1);
                coord = convertCoordinates(x, y, z);
                break;
            case 2:
                actions.setCurrentTile(new Tile(new Hex(terrainTypeA), new Hex(terrainTypeB), new Hex(TerrainType.VOLCANO)));
                actions.getCurrentTile().setOrientation(2);
                coord = convertCoordinates(x + 1, y, z - 1);
                break;
            case 3:
                actions.setCurrentTile(new Tile(new Hex(terrainTypeB), new Hex(TerrainType.VOLCANO), new Hex(terrainTypeA)));
                actions.invertTile();
                actions.getCurrentTile().setOrientation(3);
                coord = convertCoordinates(x, y, z + 1);
                break;
            case 4:
                actions.setCurrentTile(new Tile(new Hex(TerrainType.VOLCANO), new Hex(terrainTypeA), new Hex(terrainTypeB)));
                actions.getCurrentTile().setOrientation(4);
                coord = convertCoordinates(x, y, z);
                break;
            case 5:
                actions.setCurrentTile(new Tile(new Hex(terrainTypeA), new Hex(terrainTypeB), new Hex(TerrainType.VOLCANO)));
                actions.invertTile();
                actions.getCurrentTile().setOrientation(5);
                coord = convertCoordinates(x - 1, y, z + 1);
                break;
            case 6:
                actions.setCurrentTile(new Tile(new Hex(terrainTypeB), new Hex(TerrainType.VOLCANO), new Hex(terrainTypeA)));
                actions.getCurrentTile().setOrientation(6);
                coord = convertCoordinates(x, y, z - 1);
                break;
            default:
                throw new InvalidMoveException("Invalid Orientation", 20);
        }

        actions.placeTile(coord);
    }

    public   void foundSettlement(int x, int y, int z) throws InvalidMoveException {
        Location coord = convertCoordinates(x, y, z);
        actions.foundSettlement(coord);
    }

    public   void placeTotoro(int x, int y, int z) throws InvalidMoveException {
        Location coord = convertCoordinates(x, y, z);
        actions.placeTotoro(coord);
    }

    public   void placeTiger(int x, int y, int z) throws InvalidMoveException {
        Location coord = convertCoordinates(x, y, z);
        actions.placeTiger(coord);
    }

    public  void expandSettlement(int x, int y, int z, TerrainType terrainType) throws InvalidMoveException {
        Location coord = convertCoordinates(x, y, z);
        actions.expandSettlement(coord, terrainType);
    }

    public void cyclePlayerTurn(){
        if(actions.getCurrentPlayer() == whitePlayer){
            actions.setCurrentPlayer(blackPlayer);
        }
        else{
            actions.setCurrentPlayer(whitePlayer);
        }
    }


    // deletes the current list of settlements and then re-merges each and adds them to a new list
    public void mergeAllSettlement()
    {
        // error if no settlements are on the board
        if(SettlementList.isEmpty()) return; //error?

        // create a list of Hex to be filled with Settled Hexes
        SettledHexes = new ArrayList<Hex>();
        for(int i=0; i<SettlementList.size(); i++)
        {
            for (int j=0; j<SettlementList.get(i).getSize(); j++)
            {
                SettledHexes.add(SettlementList.get(i).getHexInSettlementList().get(j));
            }
        }

        // Create a new SettlementList, deletes the old one
        SettlementList = new ArrayList<Settlement>();

        // Create a array of bool to check if that hex has been merged already
        boolean[] isChecked = new boolean[SettledHexes.size()];

        for (int i=0; i<SettledHexes.size(); i++)
        {
            if(isChecked[i] == false)
            {
                isChecked[i] = true;
                SettledHexes.get(i).setParentSettlement(new Settlement());
                SettledHexes.get(i).getParentSettlement().addSettlement(SettledHexes.get(i));

                merger(SettledHexes, isChecked, i);
            }
        }
    }

    public void merger( ArrayList<Hex> SettledHexes, boolean[] isChecked, int current_position)
    {
        for (int j = 0; j < SettledHexes.size(); j++) {
            if (isChecked[j]) continue;

            Hex start_point = SettledHexes.get(current_position);
            Hex possible_adj = SettledHexes.get(j);

            Location location_start = start_point.getLocationOfHex();
            Location location_possAdj = possible_adj.getLocationOfHex();

            if ((location_possAdj.getX() == location_start.getX() && location_possAdj.getY() == (location_start.getY() - 1))
                    || (location_possAdj.getX() == (location_start.getX() + 1) && location_possAdj.getY() == (location_start.getY() - 1))
                    || (location_possAdj.getX() == (location_start.getX() + 1) && location_possAdj.getY() == location_start.getY())
                    || (location_possAdj.getX() == location_start.getX() && location_possAdj.getY() == (location_start.getY() + 1))
                    || (location_possAdj.getX() == (location_start.getX() - 1) && location_possAdj.getY() == (location_start.getY() + 1))
                    || (location_possAdj.getX() == (location_start.getX() - 1) && location_possAdj.getY() == location_start.getY()))
            {
                if(start_point.getOwner() == possible_adj.getOwner())
                {
                    isChecked[j] = true;
                    SettlementList.get(SettlementList.size() - 1).addSettlement(SettledHexes.get(j));
                    merger(SettledHexes, isChecked, j);
                }
            }
        }

        return;
    }


    public Actions getActions(){
        return actions;
    }

    public Player getWhitePlayer(){
        return whitePlayer;
    }

    public Player getBlackPlayer(){
        return blackPlayer;
    }

    public void setGameID(String gid){
        gameID = gid;
    }

    public String getGameID(){
        return gameID;
    }

    public Command getCurrentCommand(){
        return currentCommand;
    }

    public boolean isGameOver(){
        return gameOver;
    }

    public Player getWinner(){
        return winner;
    }

    public ArrayList<Settlement> getSettlementList(){
        return SettlementList;
    }

    public ArrayList<Hex> getSettledHexes(){
        return SettledHexes;
    }
}    


public class Settlement {

    private ArrayList<Hex> HexInSettlementList;
    private int size;
    private boolean hasTotoro;
    private boolean hasTiger;
    private Game parentGame;

    public Settlement(){
        if(Thread.currentThread().getName() == null){
            parentGame = new Game(" ");
        }
        else if(Thread.currentThread().getName() == GameData.getGameOne().getGameID() ){
            parentGame = GameData.getGameOne();
        }
        else if(Thread.currentThread().getName() == GameData.getGameTwo().getGameID()){
            parentGame = GameData.getGameTwo();
        }
        else {
            parentGame = new Game(" ");
        }
        parentGame.getSettlementList().add(this);
        HexInSettlementList = new ArrayList<Hex>();
        size = 0;
        hasTotoro = false;
        hasTiger = false;
    }

    public void addSettlement(Hex hex){
        hex.setParentSettlement(this);
        this.HexInSettlementList.add(hex);
        if(hex.hasTotoro()) this.hasTotoro = true;
        if(hex.hasTiger()) this.hasTiger = true;
        this.size++;
    }

    public static void removeSettlement(Hex hex){
        if(hex.isSettled())
        {
            hex.getParentSettlement().size--;
            hex.getParentSettlement().getHexInSettlementList().remove(hex);
        }
        else return; //error?
    }


    // These set functions are used for testing purposes
    public int setSize(int size){ return this.size = size; }
    public boolean setHasTotoro(boolean hasTotoro) { return this.hasTotoro = hasTotoro; }
    public boolean setHasTiger(boolean hasTiger) { return this.hasTiger = hasTiger; }


    public int getSize(){ return size; }
    public boolean getHasTotoro() { return hasTotoro; }
    public boolean getHasTiger() { return hasTiger; }
    public ArrayList<Hex> getHexInSettlementList() { return HexInSettlementList; }
}

2 个答案:

答案 0 :(得分:0)

我会避免使用静态。

正如您所说,通过制作ArrayList Static,它在所有线程之间共享。如果您想在每个线程上运行代码(或游戏)的完全独立的并行实例,则会出现此问题。

尝试从设计角度来看这个。

我建议让游戏的每个实例都实例化自己的副本,而不是试图让你的listOfCurrentSettlements静态。有几种方法可以做到这一点,但几乎所有这些方法都牺牲了使用静态变量提供的一些好处和快捷方式。一种解决方案是通过他们的构造函数将其传递给您的Settlement类:

public class Settlement {

private ArrayList<Settlement> myListOfCurrentSettlements;

public Settlement(ArrayList<Settlement> currentSettlements){
     myListOfCurrentSettlements = currentSettlements;
     mylistOfCurrentSettlements.add(this);
}

或通过其他方法:

public class Settlement {

private ArrayList<Settlement> myListOfCurrentSettlements = new ArrayList<>();

public Settlement(){
    // ...
}

public void setCurrentSettlements(ArrayList<Settlement> currentSettlements) {
    myListOfCurrentSettlements = currentSettlements;
    myListOfCurrentSettlements.add(this);
}

这显而易见:您的mergeSettlements()方法不再是静态的,现在只要您想在代码中实例化和解,您必须先确保自己携带某个地方的listOfCurrentSettlements实例,以便您可以将其传递下去。这些本质上不是坏事,但它们是你需要解决的设计问题。

您是否选择重写代码是一项由您决定的设计决策,但请注意,静态变量通常会使您在某些情况下(例如此类代码)难以扩展代码。作为Viktor suggested,,您可以始终使用ThreadLocal,但与静态变量be mindful of the risks and consequences...

非常相似

答案 1 :(得分:-1)

尝试制作名单ThreadLocal。在这种情况下,每个线程将有一个实例