怎么做我安全迭代线程内的集合?

时间:2012-12-01 23:42:06

标签: java concurrency

我有一个java / blazeds应用程序,它保留了一个数组“ _games ”,这是2个玩家订阅相同的blazeds主题。我看到的问题是连接尝试成功的比例不到40%,到目前为止我调试问题的努力都没有成功 - 它似乎无声地抛出,行为是随机的,除了第一对似乎有更多的运气连接比后续尝试。我看到了 ConcurrentModificationException ,我在这个主题上看到的内容表明我的代码存在并发问题。

在这种情况下,我在一个线程中迭代一个数组“_games”,我想知道如何安全地执行此操作,因为其他客户端将尝试随机访问此变量。

更新#2:

在调试线程内部的for循环时,看起来当前项目始终是相同的,因此迭代器没有前进和/或被后续请求重置,因此我总是= 0,或者如果使用for(游戏g: _games),然后g始终是数组中的第一项。

谢谢!

public class GameService {

private static List<Game> _games;
private static GameServiceThread thread;


public GameService(MessageTemplate template) {
    this.template = template;
}

public Game CreateOrJoinGame(GamePlayer player)
{

    Game currentGame = new Game();
    boolean isFull = false;
    for (Game g: _games)
    {

        ArrayCollection myCollection = g.myCollection;
        currentGame = g;
        if(!g.IsFull())
        {
            myCollection.add(player);

            if(g.IsFull())
            {
                isFull = true;
                currentGame.myCollection = myCollection;
                System.out.print("User Count..." + myCollection.size() + "\n");
            }
            break;
        }

    }
    if(isFull)
    {
        return currentGame;
    }
    else
    {
        Game creator = CreateGame(player);
        return creator;
    }
}


public void start() {
     if (thread == null) {
        if(_games == null)
        {
          _games = new ArrayList<Game>();
        }        
            thread = new GameServiceThread(this.template);
            thread.start();
     }
}
public void AddPlayer(GamePlayer player)
{
    _allPlayers.add(player);
}

//嵌套的静态线程类

public static class GameServiceThread extends Thread {

        @Override
    public void run() {

        this.running = true;
        while (this.running) 
        {
            for (Game g: _games)
            {
                 //do work
            }

        }
     }


    private void sendGameUpdate(final Game game) {}

此服务在flex-servlet中注册仅供参考,但我不认为这是我的问题。

<bean id="gameFeedService" class="com.acme.services.GameService">
    <constructor-arg ref="defaultMessageTemplate" />
    <flex:remoting-destination />
</bean>

修改:添加了一些有关如何添加新游戏的代码。我在GameService类中迭代_games,也在GameServiceThread中迭代。首先,当新客户端向服务发送远程游戏对象时,我在同一集合内迭代以向每个游戏接收者发送消息。每个游戏内部都是一个集合,我用它来确定游戏是否已满 - 如果它是一个新的游戏()被创建。

4 个答案:

答案 0 :(得分:1)

您可以在列表本身上进行同步。

    @Override
public void run() {

    this.running = true;
    while (this.running) 
    {
        synchronized (_games) {
            for (Game g: _games) {
             //do work
            }
        }
    }
 }

答案 1 :(得分:0)

您可能需要synchronize访问_games列表。

这是docs.oracle.com上的Synchronization教程。

答案 2 :(得分:0)

您可以从使用线程安全列表开始,例如:

List list = Collections.synchronizedList(new ArrayList(...));

检查ArrayList javadoc以获取更多信息。

答案 3 :(得分:0)

synchronized(_games) {            
    for (Game g: _games)
    {
        //do work
    }
}

OR

synchronized(getClass()) {            
    for (Game g: _games)
    {
        //do work
    }
}

都会奏效。

根据你在sendGameUpdate中所做的事情,你可能还必须在同一个对象上进行同步。