简体   繁体   English

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

[英]Trouble sharing data between instances of object, but only within current thread

Right now I have a static ArrayList in my Object class, but both of the threads are accessing/changing data to that one list. 现在,我的Object类中有一个静态ArrayList,但是两个线程都正在访问/更改该列表中的数据。 I need an ArrayList that is shared between all instances of Object, but not between instances of Object that are in different threads. 我需要一个在所有对象实例之间共享的ArrayList,但不在不同线程中的对象实例之间共享。 What's the best way to go about this? 最好的方法是什么? Here's the gist of what's going on: 这是正在发生的事情的要点:

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

I would avoid using Static. 我会避免使用静态。

Exactly as you stated, by making the ArrayList Static, it's shared between all of your threads. 就像您所说的那样,通过将ArrayList设为静态,它在所有线程之间共享。 This is a problem if you want to run entirely separate, parallel instances of your code (or game) on each thread. 如果要在每个线程上运行代码(或游戏)的完全独立的并行实例,这将是一个问题。

Try approaching this from a Design perspective. 尝试从设计角度解决这个问题。

Instead of trying to make your listOfCurrentSettlements static, I'd suggest having each instance of your game instantiate their own copy. 建议不要让您的listOfCurrentSettlements静态,而是建议让游戏的每个实例实例化自己的副本。 There are a couple ways of doing this, but nearly all of them sacrifice some of the benefits and shortcuts provided by using static variables. 有两种方法可以做到这一点,但是几乎所有方法都牺牲了使用静态变量提供的一些好处和捷径。 One solution would be to pass this down to your Settlement classes through their constructors: 一种解决方案是通过构造函数将其传递给Settlement类:

public class Settlement {

private ArrayList<Settlement> myListOfCurrentSettlements;

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

or through an additional method: 或通过其他方法:

public class Settlement {

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

public Settlement(){
    // ...
}

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

This comes at an obvious cost: your mergeSettlements() method can no longer be static, and now anytime you want to instantiate a Settlement in your code, you have to first make sure you're carrying around an instance of your listOfCurrentSettlements somewhere so you can pass it down. 这显然mergeSettlements() :您的mergeSettlements()方法不再是静态的,现在无论何时您要在代码中实例化Settlement,都必须首先确保在某个地方携带listOfCurrentSettlements的实例,这样您listOfCurrentSettlements可以把它传下来。 These aren't inherently bad things, but they are design problems for you to solve. 这些并不是天生的坏事,但它们是您要解决的设计问题。

Whether or not you choose to rewrite your code is a design decision that is up to you, but note that static variables often make it difficult to expand upon on your code in certain situations (like this one). 是否选择重写代码是由您自己决定的设计决策,但请注意,静态变量通常使在某些情况下(例如这种情况)难以扩展代码。 As Viktor suggested, you could always use ThreadLocal, but much like static variables, be mindful of the risks and consequences... 正如Viktor所建议的那样,您始终可以使用ThreadLocal,但是就像静态变量一样, 要注意风险和后果...

Try to make your list ThreadLocal. 尝试使您的列表成为ThreadLocal。 In this case you will have one instance per thread 在这种情况下,每个线程将有一个实例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM