我很确定我错过了一件小事,但我无法弄清楚这一点。我正在关注所有文档,并寻找解决方案,但我仍然遇到此错误。
现场如下: 我有一个显示线程(openGL)和逻辑更新线程。两个线程都通过arraylist进行迭代,该arraylist包含子节点。这些子节点可以添加其他子节点。如果您熟悉cocos2d系列及其场景图,那么它就是它的副本。
在CNode类之上的声明:
private List<CNode> m_children;
分配和创建
this.m_children = Collections.synchronizedList(new ArrayList<CNode>());
尝试绘制
public void visitdraw(GL2 gl){
synchronized(this.m_children){
this.draw(gl);
Iterator<CNode> it = m_children.iterator();
while (it.hasNext()){
it.next().visitdraw(gl);
}
}
}
并更新
public void visitupdate(){
synchronized(this.m_children){
this.update();
Iterator<CNode> it = m_children.iterator();
while (it.hasNext()){
it.next().visitupdate();
}
}
}
当我想删除一个问题时出现问题(即使它没有经过测试,我很确定如果我想添加一个新的节点运行时它会引发相同的异常)
public void removeChild(CNode child){
synchronized(this.m_children){
Iterator<CNode> it = this.m_children.iterator();
while(it.hasNext()){
if (it.next().equals(child)){
it.remove();
break;
}
}
}
}
我完全清楚这个系统不是处理任何事情的最好方法,但我真的被迫使用这个代码。我无法弄清楚实际问题在哪里。
任何帮助都会受到赞赏和欢迎,因为对我来说,有点难以理解为什么以前的开发人员会这样做。
答案 0 :(得分:1)
<强>问题强>
如果我没记错 - 这里的问题是,你有两个同步的块
两个块仅为自己锁定。从DB-Recources和DB-Transactions中可能知道它的锁定不是List。
因此,您可以同时调用两个不同的同步块。如果在同一个Object上执行此操作,则最终会在同一个Object上进行并发调用。
例如:
private List list
public void synchronized read() {
...iterate over list
}
public void synchronized remove() {
... remove some elements in list
}
你有两个主题A和B
然后A和B不能同时调用read(),A和B不能同时调用remove。但是他们可以同时调用read()和remove()。
的解决方案强> 的
尝试使用java.util.concurrent.locks.ReentrantReadWriteLock而不是关键字synchronized。自Java 1.5以来,Lock-Interface是新的,是现代同步的方式。
以下显示了一个拥有缓冲区的类,该缓冲区将由3个并行线程读取和更改:
class Container {
private List<String> buffer = Collections.synchronizedList(new ArrayList<String>());
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private Lock writeLock = lock.writeLock();
private Lock readLock = lock.readLock();
public void readBuffer() {
readLock.lock();
Iterator<String> it = buffer.iterator();
while(it.hasNext()) {
it.next();
}
readLock.unlock();
}
public void addOne() {
writeLock.lock();
buffer.add("next");
writeLock.unlock();
}
public void removeOne() {
writeLock.lock();
if (buffer.size() > 0) {
buffer.remove(0);
}
writeLock.unlock();
}
}
对于测试,您可以删除readLock。这将导致ConcurrentModificationException。 以下main()启动test-Threads:
public class Concurrent {
public static Container container = new Container();
public static void main(String[] args) {
new Thread(new Filler()).start();
new Thread(new Killer()).start();
new Thread(new Reader()).start();
}
static class Filler implements Runnable {
@Override
public void run() {
while(true) {
container.addOne();
}
}
}
static class Killer implements Runnable {
@Override
public void run() {
while(true) {
container.removeOne();
}
}
}
static class Reader implements Runnable {
@Override
public void run() {
while(true) {
container.readBuffer();
}
}
}
}
关键是,您在两种方法中使用相同的LockObject writeLock 。此外,这种锁允许它并行读取数据。如果有必要一次只允许一个线程读取数据,则可以使用 ReentrantLock 而不是 ReentrantReadWriteLock