从线程访问数组列表

时间:2017-03-04 20:01:15

标签: java multithreading arraylist static network-programming

我正在为我的网络课制作多人冒险游戏。我有一个客户端和一个服务器,服务器是多线程的,每当它连接一个新客户端时就会启动一个新线程。我有一个数组列表,跟踪球员,以确保没有添加新的球员。出于某种原因,当新客户端连接时,它取代旧客户端并填充新位置。这是我的这部分代码

public class ClientHandler implements Runnable{
private AsynchronousSocketChannel clientChannel;
private static String command[];
private static String name;
private static GameCharacter character;
public ClientHandler(AsynchronousSocketChannel clientChannel)
{
    this.clientChannel = clientChannel;
}

public void run(){
    try{
        System.out.println("Client Handler started for " + this.clientChannel);
        System.out.println("Messages from Client: ");
        while ((clientChannel != null) && clientChannel.isOpen()) {
            ByteBuffer buffer = ByteBuffer.allocate(32);
            Future result = clientChannel.read(buffer);
            //Wait until buffer is ready
            result.get();
            buffer.flip();
            String message = new String(buffer.array()).trim();
            if(message == null || message.equals(""))
            {
                break;
            }
            System.out.println(message);
            clientChannel.write(buffer);
            try {
                //Add the character to the routing table and the character table
                if (message.contains("connect")) {
                    System.out.println("I'm here too?");
                    command = message.split(" ");
                    name = command[1];
                    AdventureServer.userInfo.put(name, this);
                    //Check to see if this game character exists
                    GameCharacter test;
                    boolean exists = false;
                    for(int i=0; i < AdventureServer.characters.size(); i++)
                    {
                        test = AdventureServer.characters.get(i);
                        System.out.println(test.getName());
                        System.out.println(this.name);
                        if(this.name.equals(test.getName()))
                        {
                            System.out.println("already Here");
                            exists = true;
                        }
                    }
                    if (exists == true)
                    {
                        //This person has connected to the server before
                    }
                    else {
                        //Create a game character
                        System.out.println("didn't exist before");
                        character = new GameCharacter(this.name, World.getRow(), World.getCol());
                        AdventureServer.characters.add(AdventureServer.userInfo.size() - 1, character);
                        System.out.println(AdventureServer.characters.get(0).getName() + " " +AdventureServer.characters.get(1).getName());
                    }
                }

据我所知,底部的打印行会为连接的第一个客户端引发错误,但这不是问题的一部分。 这是服务器的声明

public class AdventureServer {
public static Map<String, ClientHandler> userInfo = new HashMap<>();
public static World world;
public static List<GameCharacter> characters = Collections.synchronizedList(new ArrayList<>());
public static void main(String args[]) {
    //Create the games map that all of the users will exist on
    world = new World(args[0]);

    System.out.println("Asynchronous Chat Server Started");
    try {
        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
        InetSocketAddress hostAddress = new InetSocketAddress("192.168.1.7", 5000);
        serverChannel.bind(hostAddress);
        while (true)
        {
            System.out.println("Waiting for client to connect");
            Future acceptResult = serverChannel.accept();
            AsynchronousSocketChannel clientChannel = (AsynchronousSocketChannel) acceptResult.get();
            new Thread (new ClientHandler(clientChannel)).start();
        }
    } catch (Exception e) {
        System.out.println("error interrupted");
        e.printStackTrace();
        System.exit(0);
    }
}
}

这是我的游戏角色构造函数

public class GameCharacter {
public static int xpos;
public static int ypos;
private static String name;
private static int rowSize;
private static int columnSize;
static List<String> inventory = new ArrayList<>();

//Constructor
GameCharacter(String n, int rSize, int cSize)
{
    xpos = 0;
    ypos = 0;
    name = n;
    rowSize = rSize;
    columnSize = cSize;
}

GameCharacter()
{
    xpos = 0;
    ypos = 0;
    name = "billybob";
    rowSize = 10;
    columnSize = 10;
}

2 个答案:

答案 0 :(得分:0)

作为可读性,可测试性和样式的问题,我还建议您不要直接访问属于另一个类的数据结构。而不是

   Adventureserver.characters.add(blah blah)

我建议将字符设置为Adventureserver的私有字段,然后创建一个方法来添加或删除字符。事实上,我倾向于不让角色静态 - 没有真正的优势,你可能在某些时候想要运行多个Adventureserver。

有点像:

public class AdventureServer {
<...>
private List<GameCharacter> characters = Collections.synchronizedList(new ArrayList<>);

<...>
public void addCharacter(GameCharacter char) {
   <... error checking ...>
   characters.add(char);
}
public void removeCharacter(GameCharacter char) {
   <... implementation ... >
}
public boolean isCharacterHere(GameCharacter char) {
}
public List<GameCharacter> getCharacters() {
  <... you could either return characters here, or a copy of it,
   depending upon how paranoid you want to be >

答案 1 :(得分:0)

您可以尝试:

public static volatile List<GameCharacter> characters = Collections.synchronizedList(new ArrayList<>());

更新: 问题是您使用的是非同步的HashMap userInfo。 从以下地址更改该行:

AdventureServer.characters.add(AdventureServer.userInfo.size() - 1, character);

要:

AdventureServer.characters.add(character);

或者让您的HashMap同步:

public static Map<String, ClientHandler> userInfo = Collections.synchronizedMap(new HashMap<>());

所有这些静态声明都会出现问题,您应该删除它们。通常,您应该避免使用静态。

ClientHandler的:

private static String command[];
private static String name;
private static GameCharacter character;

GameCharacter:

public static int xpos;
public static int ypos;
private static String name;
private static int rowSize;
private static int columnSize;
static List<String> inventory = new ArrayList<>();

只是旁注,这样你的类更像Java代码应该是这样的:

import java.util.ArrayList;
import java.util.List;

public class GameCharacter {
private int xpos;
private int ypos;
private String name;
private int rowSize;
private int columnSize;

private List<String> inventory = new ArrayList<>();

// Constructor
GameCharacter(String n, int rSize, int cSize) {
    this.xpos = 0;
    this.ypos = 0;
    this.name = n;
    this.rowSize = rSize;
    this.columnSize = cSize;
}

GameCharacter() {
    this.xpos = 0;
    this.ypos = 0;
    this.name = "billybob";
    this.rowSize = 10;
    this.columnSize = 10;
}

public int getXpos() {
    return xpos;
}

public void setXpos(int xpos) {
    this.xpos = xpos;
}

public int getYpos() {
    return ypos;
}

public void setYpos(int ypos) {
    this.ypos = ypos;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getRowSize() {
    return rowSize;
}

public void setRowSize(int rowSize) {
    this.rowSize = rowSize;
}

public int getColumnSize() {
    return columnSize;
}

public void setColumnSize(int columnSize) {
    this.columnSize = columnSize;
}

public List<String> getInventory() {
    return inventory;
}

public void setInventory(List<String> inventory) {
    this.inventory = inventory;
}

}