简体   繁体   中英

Why does synchronizedList doesn't work as expected?

I have written the below program that would create three threads and start them. The task for each thread is to iterate a list of strings. This list is a synchronizedList. When I run the program, I still see the thread output not synchronized. ie, Before the first thread iterates through everything in the list, the second one interrupts and third one and so forth

import java.util.*;
public class Program implements Runnable{
    List<String> stars = Collections.synchronizedList(new ArrayList<String>());
    public static void main(String[] ars){
        System.out.println(Thread.currentThread().getName());   
        Program p = new Program();
        p.init();
    }
    public void init(){
        stars.add("Tom Hanks");
        stars.add("Bruce Lee");
        stars.add("Matthew");
        stars.add("fasbender");
        stars.add("pitt");
        Thread one = new Thread(this);
        one.setName("First");
        Thread two = new Thread(this);
        two.setName("Two");
        Thread three = new Thread(this);
        three.setName("Three");
        one.start();
        two.start();
        three.start();

    }
    public void run(){
        for(int i=0;i<stars.size();i++){
            System.out.println(stars.get(i)+" "+Thread.currentThread().getName());
        }
    }
}

I was expecting the output as: main Tom Hanks First Bruce Lee First Matthew First fasbender First pitt First Tom Hanks Second Bruce Lee Second Matthew Second fasbender Second pitt Second Tom Hanks Third Bruce Lee Third Matthew Third fasbender Third pitt Third

But when i run the program, the actual output is like:

main
Tom Hanks First
Bruce Lee First
Matthew First
fasbender First
pitt First
Tom Hanks Three
Tom Hanks Two
Bruce Lee Three
Bruce Lee Two
Matthew Three
Matthew Two
fasbender Three
fasbender Two
pitt Three
pitt Two

The List is synchronized all right, but the order of access from the threads point of view is unpredictable and depends solely on the thread scheduler. So if you want consistent behaviour then you have to synchronize on the list itself (enclose it in a synchronized block with the list as the parameter.
Check out the docs for more details.
Also, check this answer out for more explanation.

Iteration over the synchronized list is not atomic. The client has to provide the locking.

If you look at the source documentation of Collections.synchronizedList , it uses the returned list as lock (mutex). So the client code can make the iteration over it atomic by applying the synchronized block using the returned list.

From the Java Code for SynchronizedCollection

SynchronizedCollection(Collection<E> c) {
    this.c = Objects.requireNonNull(c);
    mutex = this;
}

SynchronizedList extends SynchronizedCollection locks on the mutex.

static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
...
...
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
}

In this example, even after the synchronized on the client code, the order of thread operation is still non deterministic and depends on the thread scheduler. Thread 3 can be called before Thread 2.

public void run(){

        synchronized (stars) {
            for(int i=0;i<stars.size();i++){
                System.out.println(stars.get(i)+" "+Thread.currentThread().getName());
            }
        }
    } 

The output could be.

main
Tom Hanks First
Bruce Lee First
Matthew First
fasbender First
pitt First
Tom Hanks Three
Bruce Lee Three
Matthew Three
fasbender Three
pitt Three
Tom Hanks Two
Bruce Lee Two
Matthew Two
fasbender Two
pitt Two

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM