What is the use of Collections.synchronizedList() method? It doesn't seem to synchronize the list

眉间皱痕 提交于 2019-12-02 20:33:25

A synchronized list only synchronizes methods of this list.

It means a thread won't be able to modify the list while another thread is currently running a method from this list. The object is locked while processing method.

As an example, Let's say two threads run addAllon your list, with 2 different lists (A=A1,A2,A3 and B=B1,B2,B3) as parameter.

  • As the method is synchronized, you can be sure those lists won't be merged randomly like A1,B1,A2,A3,B2,B3

  • You don't decide when a thread handover the process to the other thread. Each method call has to fully run and return before the other one could run. So you can either get A1,A2,A3,B1,B2,B3 or B1,B2,B3,A1,A2,A3 (As we don't know which thread call will run first).

In your first piece of code, both threads runs on the same time. And both try to add an element to the list. You don't have any way to block one thread except the synchronization on the add method so nothing prevent thread 1 from running multiple add operation before handing over the process to thread 2. So your output is perfectly normal.

In your second piece of code (the uncommented one), you clearly state that a thread completely lock the list from the other thread before starting the loop. Hence, you make sure one of your thread will run the full loop before the other one could access the list.

Collections.synchronizedList() will synchronize all the accesses to the backed list except while iterating which still needs to be done within a synchronized block with the synchronized List instance as object's monitor.

So for example here is the code of the add method

public boolean add(E e) {
    synchronized (mutex) {return c.add(e);}
}

This guarantees serial access to the backed list, so if your 2 threads call add at the same time, one thread will acquire the lock, add its element and release the lock then the second thread will be able to acquire the lock and add its element that is why you get alternatively one and two in your output.

When you uncomment the synchronized block, the code is then

synchronized(o) {
    for(int i=0;i<100;i++){
        ...
    }
}

In this case the thread that could acquire the lock on o first will execute the entire for loop before releasing the lock (except if an exception is thrown), allowing the other thread to execute the content of its synchronized block, which is why you get 100 consecutive times one or two then 100 consecutive times the other value.

Observable behavior is absolutely correct - synchronized approach you demonstrate in code sample does not the same as synchronizedList. In first case you synchronize the whole for-statement, so only one thread will execute it simultaniously. In the second case you synchromize collection methods itself - that's what synchronizedList stands for. So be sure that add method is synchronized - but not the for method!

According to previous answers you need to synchronize the synList from access thread tOne and tTwo. In this case, you can use the monitor pattern to provides a security access - in order for threads.

Below I adapted your code to be share with others that have same problems. In this code I used only the synList to control access in synchronized way. Notice that it is not necessary create other object to ensure the order access from synList. To complement this question, see book Java Concurrency in Practice jcip chapter 4 that talks about monitor design patterns that is inspired by Hoare's work

public class SynTest {
public static void main(String []args){
    final List<String> synList= Collections.synchronizedList(new ArrayList<>());

    Thread tOne=new Thread(() -> {
        synchronized (synList) {
            for (int i = 0; i < 100; i++) {
                System.out.println(synList.add("add one" + i) + " one");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread tTwo=new Thread(()->{
        synchronized (synList) {
            for(int i=0;i<100;i++){
                System.out.println(synList.add("add two"+i)+" two");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            }

    });
    tOne.start();
    tTwo.start();
}

}

This is a cool little example based on the original example and accepted answer to show what purpose synchronizedList serves.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class SynTest {
    public static void main(String []args) throws InterruptedException
    {
        final List<String> list = new ArrayList<>();
        final List<String> synList = Collections.synchronizedList(new ArrayList<>());

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                list.addAll(Arrays.asList("one", "one", "one"));
                synList.addAll(Arrays.asList("one", "one", "one"));
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                list.addAll(Arrays.asList("two", "two", "two"));
                synList.addAll(Arrays.asList("two", "two", "two"));
            }
        });

        t1.start();
        t2.start();

        Thread.sleep(1000);
        System.out.println(list);
        System.out.println(synList);
    }
}

The original list ends up having undefined behaviour with results such as:

[one, one, one] // wrong!
[one, one, one, null, null, null] // wrong!
[two, two, two] // wrong!
[one, one, one, two, two, two] // correct

While the synchronized synList has a synchronised addAll method and always produces one of the two correct results:

[one, one, one, two, two, two] // correct
[two, two, two, one, one, one] // correct
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!