【问题标题】:how to do I safely iterate over a collection inside a thread?如何安全地迭代线程内的集合?
【发布时间】:2012-12-01 23:42:06
【问题描述】:

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

在这种情况下,我在一个线程内迭代一个数组“_games”,我想知道如何安全地做到这一点,因为其他客户端会随机尝试访问这个变量。

更新 #2:

在调试线程内的 for 循环时,似乎当前项目始终相同,因此迭代器不会前进或被后续请求重置,因此 i 始终 = 0 ,或者如果使用 for(Game 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 类和 GameServiceThread 中迭代了 _games。首先,当新客户端向服务发送远程游戏对象时,我在同一个集合中进行迭代以向每个游戏接收者发送消息。每个游戏内部都有一个集合,我用它来确定游戏是否已满 - 如果它是一个新的 Game() 被创建。

【问题讨论】:

  • 如何将游戏添加到_games 列表中?
  • 添加了一个编辑 - 如果您需要更多信息,请告诉我@KeithRandall

标签: java concurrency


【解决方案1】:

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

    @Override
public void run() {

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      你可以从使用线程安全的 List 开始,例如:

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

      查看ArrayList javadoc了解更多信息。

      【讨论】:

      • 这仅在您想要的唯一同步是对列表本身的操作(添加、删除等)时才有效。也就是说,您不需要程序中的任何代码段以原子方式执行。
      • @xagyg - 你说得对,它可以解决一个问题,但不足以解决所有问题。
      【解决方案4】:
      synchronized(_games) {            
          for (Game g: _games)
          {
              //do work
          }
      }
      

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

      两者都可以。

      根据您在 sendGameUpdate 中执行的操作,您可能还必须在同一对象上同步。

      【讨论】:

        猜你喜欢
        • 2011-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-10
        • 2014-03-18
        • 2011-04-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多