我有两个独立的主题:
我的逻辑线程调用ArrayList.remove(),所以我认为当绘图线程绘制时,可能会有机会崩溃,因为索引不再存在。
代码示例:
drawingThread extends Thread {
logicThread = new LogicThread;
logicThread.start();
public void run(){
while(running) {
for(int i=0; i<npc.size(); i++){
npc.get(i).callDraw();
}
}
// stop logicThread when out of gameloop
logicThread.running = false;
}}
LogicThread extends Thread {
public void run(){
while(running){
for(int i=0; i<npc.size();i++){
if(npc.get(i).isDead()){
npc.remove(i);
}
npc.trimToSize();
}
Collection.sort(npc);
}
}}
哪种方法可以防止异常同步或 trycatch ? 使用其中一个还有什么好处吗?
synchronized(logicThread) {
for(int i=0; npc.size(); i++) {
npc.callDraw();
}}
或
try {
npc.callDraw();
} catch(Exception e) {}
答案 0 :(得分:3)
仅供注意:使用迭代器从集合中删除项目:
while (it.hasNex()) {
if(...) {
it.remove();
如果可接受,则捕获IndexOutOfBound异常有效。另一种方法 - 在绘图线程中创建数组副本,这将保证您不会获得IndexOutOfBound。你可以在绘图循环中添加一个检查isDead()
答案 1 :(得分:1)
您应该绝对同步访问权限,但您需要在两个线程中执行此操作,并且需要在共享对象上进行同步,例如
synchronized(npc) {
// Do something that accesses or modifies npc
}
看看你的具体例子,我建议你可能不希望这样做,因为你需要锁定for循环。您可能需要将同步移动到共享的npc对象中。
是否有任何理由你不能在第一个线程中注意哪些npcs已经死亡,然后在退出for循环后将其从列表中删除。如果可以的话,最好避免单独的线程和同步。
答案 2 :(得分:1)
你不应该抓住ArrayIndexOutOfBoundsException
,因为这是一个未经检查的异常,这应该用于编程错误,只有在发生错误时抛出(注意这有点争议,但上面是什么有效的Java,布洛赫告诉我们)。
如果您正在采取的操作花费的时间很少,请使用同步。如果循环花费大量时间,请使用同步来复制列表,然后迭代副本而不是原始副本。
您可能遇到的另一个问题(取决于您使用的列表类型)是ConcurrentModificationException,当您从正在迭代的列表中删除元素时会发生这种情况。
另请注意,如果在两个线程中使用共享对象时不同步共享对象,则可能会产生奇怪的内存效果(例如查看不完整的对象)。 Goetz实践中的Java Concurrency是一本很好的书,它更多地讲述了这个被广泛误解的概念。
使用synchronized块的另一种解决方案是使用CopyOnWriteArrayList,它将阻止ConcurrentModificationException和内存效果。请注意,为了使用“复制效果”,您需要使用迭代器来删除元素。
答案 3 :(得分:1)
你需要线程吗?如果您调用绘图循环后跟逻辑循环(串行),您是否获得足够的帧速率?假设你有一个双缓冲显示器。通常应该在异步进行的地方使用线程(比如等待服务器响应),这是你无法控制的。在这种情况下,您可以控制事情发生的时间和顺序。
答案 4 :(得分:1)
在整个绘图/逻辑块上进行同步将否定线程的好处,捕获异常可能会导致UI不一致(更不用说管理代码了)。
当迭代另一个线程可能修改的集合时,先复制它!
List drawList = new ArrayList(npc);
for(int i=0; i<drawList.size(); i++){
drawList.get(i).callDraw();
}
您可能仍需要同步复制操作;风险要低得多,但仍存在竞争条件,导致副本中出现空白。 Collections.synchronizedList()可以将常规列表转换为同步列表,但会牺牲所有操作的速度。
如果使用synchronizedList()的性能成为问题,您只需手动同步复制和删除操作即可。