0

Got a code with Main Thread and myThread. When some button is pressed in Main Thread myThread.start() is called. onCameraFrame constantly gets and saves color values of frames to ArrayList<Double> rV. In myThread i need sout(rV), do some stuff with it and clean rV every 6 seconds.

I used iterator to do such thing, but still i get java.util.ConcurrentModificationException at sout line in myThread. Note it happens at random time. For example after button was pressed sout might work just fine for 1 sec or for 5 minutes, and then - exception.

My suggestion is that rV is used by myThread and by Main Thread (onCameraFrame) at one time. So it collapses.

Need an advice. Struggling with this for hours.

Here's the code.

public class Camera extends Activity implements CvCameraViewListener2 {

@Override
public void onCreate(Bundle savedInstanceState) {
    ..
    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ..
            myThread = new Thread(mRunnable);
            myThread.start();
        }
    ..
}

Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        Thread thisThread = Thread.currentThread();
        while (myThread==thisThread) {
            try {thisThread.sleep(6000);} 
            catch ..}
            mButton.post(new Runnable() {
                @Override
                public void run() {
                    /*logging*/
                    if (!rV.isEmpty()){
                        System.out.println("rV"+"("+rV.size()+")={"+rV.toString()+"}");
                    }

                    *//*clean data*//*
                    for (Iterator<Double> it = rV.iterator(); it.hasNext();) {
                        while(it.hasNext()){
                            Double t = it.next();
                            it.remove();
                        }
                    }
                }
            });
        }
    }
};

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    ..
    if (condition) {
        rV.add(somevalue);
    }
    return inputFrame.rgba();
}
Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
idementia
  • 851
  • 1
  • 6
  • 12
  • Have you tried using Queue (Dequeue)? You can push all the new data from your populator thread and pop the data from the other side? Reading data thread can pop the data, do something on it and then populate it in another collection, if needed. – rohitmohta Feb 27 '16 at 19:29

3 Answers3

0

You have to synchronize the access to the data structure. A good thing to do is to use a Semaphore(java.concurrent package). Semaphore s = new Semaphore(1); -> 1 permit.

In your thread you aquire the permit: s.aquire() - If there is no permit, the thread stops here. After your work you release the permit: s.release(). And thats it, only one thread can access the data now

Henning Luther
  • 2,067
  • 15
  • 22
0

There are two potential issues here. First: ConcurrentModificationException caused by removing from your ArrayList while iterating over the ArrayList. See this question to resolve this first issue.

The second issue is that the ArrayList is not thread-safe. So, you cannot modify 'rV' while iterating over it without it throwing the exception. Use a thread-safe implementation from the java.util.concurrent package instead. For instance, use an ArrayBlockingQueue. See the tutorial on the Concurrency for more details

@DDsix recommends using a CopyOnWriteArrayList. This may be appropriate in some situations, but inappropriate in others. The JavaDocs for CopyOnWriteArrayList explain it this way:

all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array... This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads.

Community
  • 1
  • 1
Austin
  • 8,018
  • 2
  • 31
  • 37
  • Most of the thread safe collection will allow you to do a dirty read (read w/o locking), but removing and inserting will call for a lock. – rohitmohta Feb 27 '16 at 19:31
  • `ArrayBlockingQueue` certainly doesn't have this issue... Which collection are you referring to? – Austin Feb 27 '16 at 19:54
  • @AustinD, i tried `ArrayBlockingQueue` (say, ABK) i got another issue. I cannot declare ABK in Main Thread since its size (number of frames color values) directly depends on camera fps which varies constantly. So i tried to create ABK in myThread and got the same issue cause in order to clone rV into ABK i have to loop through rV in myThread. This code i pasted in myThread. `ArrayBlockingQueue rVBQ = new ArrayBlockingQueue(rV.size()); for (Double d : rV) { rVBQ.add(d); }` – idementia Feb 27 '16 at 22:00
  • Why not just use `rV.clear()` and then remove `for (Iterator it = rV.iterator(); it.hasNext();) {while(it.hasNext()){ Double t = it.next(); it.remove(); } }` – Austin Feb 28 '16 at 00:43
  • Well, i didnt get why you say that - iterator usage was fine, the error popped out at `sout(rV.size+rV.toString)`. Anyway, i noticed that ABQ doesnt have to be _exact_ size as rV, it is _max_ capacity of ABQ which must be specified. I set it to 1000 and so i didnt use ArrayList at all and was able to get access to ABQ in both Main and myThread without any exceptions. Thanks for the tip! – idementia Feb 28 '16 at 08:25
0

You can either use CopyOnWriteArrayList which is basically a thread safe ArrayList, or use Collections.synchronizedList(new ArrayList<Object>()); to create your List (this wouldn't be concurrent, but synchronized, though)

DDsix
  • 1,966
  • 1
  • 18
  • 23