1

为什么会有这段代码:

public synchronized void update() {
        for(T event : eventQueue)
        {
            processEvent(event);
        }
        events = eventQueue;
        eventQueue = new LinkedList<T>();
}

运行方式与此代码不同:

public synchronized void update() {
            for(T event : eventQueue)
            {
                processEvent(event);
            }
            events = eventQueue;
            eventQueue.clear();
}

第一个版本工作得很好,但第二个版本不行。这eventQueue.clear();会导致应用程序不接收任何事件并以Concurrent Exception.

我的应用程序有两个线程。UI 线程和 GameLoop 线程。UI线程将事件添加到eventQueue这样的:

public synchronized void addEvent(T newEvent) {
            eventQueue.add(newEvent);
}

GameLoop 线程调用 update 方法来events获取eventQueue.

所有代码都可以从这个网站查看:http ://entropyinteractive.com/2011/02/game-engine-design-input/

这对我来说似乎有点神秘,因为我认为eventQueue = new LinkedList<T>();并且eventQueue.clear();都会导致空LinkedList?我相信它与建立一个新的参考有关(但为什么?!)。

4

3 回答 3

3

因为在这段代码中:

public LinkedList<T> getEvents()
{
    return events;
}

您返回的是原始列表,而不是副本。如果您clear()使用该列表,则会导致问题,因为您正在从列表中删除内容(更重要的是,更改列表的大小),而另一个线程正在使用它。

请注意,此函数 is not synchronized,因此您甚至无法安全地从中返回副本,因为在您复制它时原始列表可能会发生变化(更改引用是atomicupdate() ,因此在您的方法中这样做是安全的) .

您可以从同步方法返回一个副本,如下所示:

public synchronized LinkedList<T> getEvents()
{
    return new LinkedList<T>(events);
}

但这引入了不必要的复制和锁定。这是否重要取决于您是否更关心防御性编码或性能要求。我认为他们这样做是出于性能原因。

于 2013-03-28T17:30:45.993 回答
0

这是你的问题

  events = eventQueue;
  eventQueue.clear()

分配events = eventQueue后,您将通过以下方式逃避对eventQueue公众的引用

public LinkedList<T> getEvents()
{
    return events;
}

getEvents()一个线程可以在实际存在的队列上进行迭代,eventQueue而另一个线程可以调用addEvent,并且由于synchronizedin addEvent 不再与您的迭代器匹配,因此getEvents()您将获得 comodification。

我的建议是,如果您真的想发布您的活动,请使用新系列。

  events = new LinkedList<T>(eventQueue);
  eventQueue.clear()
于 2013-03-28T17:35:23.007 回答
0

如果你不保护所有的使用eventQueue,比如分发引用getEvents(),你应该尝试ConcurrentLinkedList

于 2013-03-28T17:36:26.690 回答