Frage

Simply I have a shared java LinkedList in an Android app that has 2 Threads, a render thread and an update thread. Both threads run continously and keep on iterating over the shared LinkedList. Sometimes the update thread has to remove an object from the LinkedList. How do I code the program such to keep multithreaded performance but allowing this to happen. I keep receiving a java.util.ConcurrentModificationException from the render thread because I am guessing the object is being removed while I am trying to render it.

Please note, I use iterators in my code...

public void run()
{
  if(Thread.currentThread() == renderThread)
  {
    while(true)
    {
      for(Iterator<GameObject> it = objects.iterator(); it.hasNext();)
      {
         it.next().render();
      }
    }
  }
  if(Thread.currentThread() == updateThread)
  {
    while(true)
    {
      for(Iterator<GameObject> it = objects.iterator(); it.hasNext();)
      {
         GameObject o = it.next();

         it.next().update();

         if(o.shouldBeRemoved())
             it.remove();
      }
    }
  }
}

One possible solution is to catch the Exception and just ignore it, but I feel that is the cheaters way out since exceptions should be used for exceptional circumstances, not flow.

War es hilfreich?

Lösung

You are modifying your list structurally in update thread while, at the same time, the render thread has created an Iterator and is having fun iterating your objects. You're getting ConcurrentModificationException as a punishment for not handling synchronization issues.

Try searching for any example with synchronized block/method and wait()/notify().

UPD: Ok, here is a simple modification of your code, which does not throw an exception. This is not a good example of List synchronization, you do not gain much performance (update and render operations are still in different threads, but performed sequentially), and there are few things to consider - if any other thread tries to modify your List, you will still get a concurrent exception.

In your launcher class define two threads:

private Thread t1, t2;

Code that lauches threads (e.g., inside public static void main(..)):

final List<GameObject> list = initList();//some initialization
t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            myMultipurposeMethod(list);
        }
    });

 t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            myMultipurposeMethod(list);
        }
    });
 t1.start();
 t2.start();

Now a simplified version of your GameObject class. Pay attention to synchronized blocks:

class GameObject {

    private int id;
    private static int count = 0;
    private boolean shouldDelete = false;
    private Object monitor = new Object();//will be used to lock critical section

    public GameObject() {
        id = ++count;
    }


    public void update(Thread thread) {
        synchronized (monitor) {
            shouldDelete = Math.random() < 0.5;//just a simple randomization
            System.out.println(thread.getName() + " updates GameObject. should be removed =" + shouldBeRemoved());
        }
    }

    public boolean shouldBeRemoved() {
        synchronized (monitor) {
            return shouldDelete;
        }
    }

    public void render(Thread thread) {
        System.out.println(thread.getName() + " renders GameObject " + id);
    }
}

And finally, your multipurpose method:

public void myMultipurposeMethod(List<GameObject> ls) {
    synchronized (ls) {
        if (Thread.currentThread().equals(t1)) {
            while (true) {                    
                for (Iterator<GameObject> i = ls.iterator(); i.hasNext(); ) {
                    i.next().render(Thread.currentThread());
                    ls.notify();
                }
                try {
                    ls.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else if (Thread.currentThread().equals(t2)) {
            while (true) {
                for (Iterator<GameObject> i = ls.iterator(); i.hasNext(); ) {
                    GameObject o = i.next();
                    o.update(Thread.currentThread());
                    if (o.shouldBeRemoved()) {
                        i.remove();
                        System.out.println("game object with id=" + o.id + " marked as garbage and was removed");
                    }
                }
                ls.notify();

                try {
                    ls.wait();
                } catch (InterruptedException e) {
                    e.printStackTract();
                }
            }
        }
    }
}

That will give you an illusion of parallel execution and prevent your Exception from happening, if you follow the requirements given in the preamble. However, in order to achieve a real performance gains, you would be better off following @isnot2bad advices given in the comments to OP.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top