为什么syncedList不能按预期工作?

时间:2019-11-08 19:21:08

标签: java arraylist

我编写了下面的程序,该程序将创建三个线程并启动它们。每个线程的任务是迭代字符串列表。该列表是一个syncedList。当我运行程序时,我仍然看到线程输出未同步。即,在第一个线程迭代列表中的所有内容之前,第二个线程中断,第三个线程以此类推

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());
        }
    }
}

我期望输出为:         主要     汤姆·汉克斯     李小龙第一     马修·菲斯特(Matthew First)     Fasbender First     皮特第一     汤姆·汉克斯Second     李小龙第二     马修·第二     Fasbender Second     皮特秒     汤姆·汉克斯(Tom Hanks)Third     李小龙第三     马修·第三     fasbender第三     皮特第三

但是当我运行程序时,实际输出如下:

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

2 个答案:

答案 0 :(得分:1)

List可以完全同步,但是从线程角度来看访问的顺序是不可预测的,并且仅取决于线程调度程序。因此,如果要保持一致的行为,则必须在列表本身上synchronize(将列表作为参数放在synchronized块中。
请查看docs了解更多详细信息。
另外,请查看this的答案以获取更多说明。

答案 1 :(得分:0)

在同步列表上的迭代不是原子的。客户端必须提供锁定。

如果您查看Collections.synchronizedList的源文档,它将返回的列表用作锁(互斥体)。因此,客户端代码可以通过使用返回的列表应用同步块来使它的原子迭代成为可能。

来自SynchronizedCollection的Java代码

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

SynchronizedList扩展了互斥锁上的SynchronizedCollection锁定。

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

在此示例中,即使在客户端代码上同步之后,线程操作的顺序仍然是不确定的,并且取决于线程调度程序。线程3可以在线程2之前调用。

public void run(){

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

输出可能是

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