Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ArrayList vs Vector - does this illustrate difference in synchronization?

I'm trying to understand the difference in behaviour of an ArrayList and a Vector. Does the following snippet in any way illustrate the difference in synchronization ? The output for the ArrayList (f1) is unpredictable while the output for the Vector (f2) is predictable. I think it may just be luck that f2 has predictable output because modifying f2 slightly to get the thread to sleep for even a ms (f3) causes an empty vector ! What's causing that ?

public class D implements Runnable {
   ArrayList<Integer> al;
   Vector<Integer> vl;

   public D(ArrayList al_, Vector vl_) {
      al = al_;
      vl = vl_;
   }

   public void run() {
      if (al.size() < 20)
         f1();
      else
         f2();
   } // 1

   public void f1() {
      if (al.size() == 0)
         al.add(0);
      else
         al.add(al.get(al.size() - 1) + 1);
   }

   public void f2() {
      if (vl.size() == 0)
         vl.add(0);
      else
         vl.add(vl.get(vl.size() - 1) + 1);
   }

   public void f3() {
      if (vl.size() == 0) {
         try {
            Thread.sleep(1);
            vl.add(0);
         } catch (InterruptedException e) {
            System.out.println(e.getMessage());
         }
      } else {
         vl.add(vl.get(vl.size() - 1) + 1);
      }
   }

   public static void main(String... args) {
      Vector<Integer> vl = new Vector<Integer>(20);
      ArrayList<Integer> al = new ArrayList<Integer>(20);
      for (int i = 1; i < 40; i++) {
         new Thread(new D(al, vl), Integer.toString(i)).start();
      }
   }
}
like image 1000
Sridhar Avatar asked Sep 24 '26 07:09

Sridhar


1 Answers

To answer the question: Yes vector is synchronized, this means that concurrent actions on the data structure itself won't lead to unexpected behavior (e.g. NullPointerExceptions or something). Hence calls like size() are perfectly safe with a Vector in concurrent situations, but not with an ArrayList (note if there are only read accesses ArrayLists are safe too, we get into problems as soon as at least one thread writes to the datastructure, e.g. add/remove)

The problem is, that this low level synchronization is basically completely useless and your code already demonstrates this.

if (al.size() == 0)
   al.add(0);
else
   al.add(al.get(al.size() - 1) + 1);

What you want here is to add a number to your datastructure depending on the current size (ie if N threads execute this, in the end we'd want the list to contain the numbers [0..N)). Sadly that does not work:

Assume that 2 threads execute this code sample concurrently on an empty list/vector. The following timeline is quite possible:

T1: size() # go to true branch of if
T2: size() # alas we again take the true branch.
T1: add(0)
T2: add(0) # ouch

Both execute size() and get back the value 0. They then go into the true branch of the and both add 0 to the datastructure. That's not what you want.

Hence you'll have to synchronize in your business logic anyhow to make sure that size() and add() are executed atomically. Hence the synchronization of vector is quite useless in almost any scenario (contrary to some claims on modern JVMs the performance hit of an uncontended lock is completely negligible though, but the Collections API is much nicer so why not use it)

like image 132
Voo Avatar answered Sep 26 '26 19:09

Voo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!