0

After the following code

ArrayList<String> foo, bar, test;
foo = new ArrayList<String>(Arrays.asList("foo")); 
bar = new ArrayList<String>(Arrays.asList("bar"));
test = new ArrayList<String>(Arrays.asList("test"));

Thread 1 runs:

foo = new ArrayList<String>(bar);

Thread 2 runs:

bar = new ArrayList<String>(test);

Can this cause an exception if Thread 2 changes value of bar while it is being used by Thread 1?

activity
  • 2,653
  • 3
  • 20
  • 44
  • This is concurrent access to a *reference*, not the list itself. – Oliver Charlesworth Mar 30 '17 at 11:49
  • No excpetions will be thrown for this, but if this is a concern for you you have probably created a [race condition](http://stackoverflow.com/questions/34510/what-is-a-race-condition). – RobCo Mar 30 '17 at 11:52
  • 2
    "changes value of bar" - bar will not be affected if the reference is changed; the other threads will still have a reference to the original list. So "no". – Steve Smith Mar 30 '17 at 11:52

2 Answers2

1

You won't get a concurrent modification exception, since the list instances are never mutated. Only references are exchanged.

However you still have a data race:

  • Either the assignment in Thread1 runs first. Thread1 will capture the initial bar object and create a new `foo´ which is the copy of that.
  • Or the assignment in Thread2 will run first, which means it creates a new list which is stored in the bar reference, and when Thread1 runs later on it will end up with a copy of that one, which itself is a copy of the test list.
Matthias247
  • 9,836
  • 1
  • 20
  • 29
0

It is not a problem at all. To understand what happens you need to know how java handles such structures as ArrayList (and other collections).

When you create a new ArrayList variable, the variable contains only the link to the segment of RAM where the real data is kept. When you pass the variable to a constructor or another method, java copies the value of the variable, and the parameter is initialized with this value. So when you assign a new list to the parameter, you don't change the value of the original variable:

public class Main {

    public static void main(String [] args) throws IOException {
        List<String> foo, bar, test;
        foo = new ArrayList<String>(Arrays.asList("foo")); 
        bar = new ArrayList<String>(Arrays.asList("bar"));
        test = new ArrayList<String>(Arrays.asList("test"));

        CountDownLatch cdl = new CountDownLatch(1);

        Thread r1 = new Thread(new MyThread("foo = new ArrayList(bar)", bar, foo, cdl));
        Thread r2 = new Thread(new MyThread("bar = new ArrayList(test)", test, bar, cdl)); 
        r1.start();
        r2.start();

        cdl.countDown();
        System.out.println("Size of foo is " + foo.size());
        System.out.println("Size of bar is " + foo.size());
    }
}

class MyThread implements Runnable {

    private List<String> src;
    private List<String> dst;
    private CountDownLatch cdl;
    private String msg;

    public MyThread(String msg, List<String> src, List<String> dst, CountDownLatch cdl) {
        this.msg = msg;
        this.src = src;
        this.dst = dst;
        this.cdl = cdl;
    }

    @Override
    public void run() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        dst = new ArrayList<String>(src);
        dst.add("test");
        System.out.println(msg + " and my size is " + dst.size());
    }

}

Output:

bar = new ArrayList(test) and my size is 2
foo = new ArrayList(bar) and my size is 2
Size of foo is 1
Size of bar is 1
neshkeev
  • 6,280
  • 3
  • 26
  • 47