我已经编写了下面的程序,该程序将创建三个线程并启动它们。每个线程的任务是迭代一个字符串列表。此列表是一个synchronizedList。当我运行程序时,我仍然看到线程输出没有同步。即,在第一个线程遍历列表中的所有内容之前,第二个线程中断,第三个线程中断,依此类推
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 Firstfasbender First皮特第一汤姆·汉克斯第二李小龙第二Matthew Secondfasbender Second皮特秒汤姆·汉克斯第三李小龙第三Matthew Thirdfasbender Third皮特第三
但当我运行程序时,实际输出是这样的:
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
List
是完全同步的,但从线程的角度来看,访问顺序是不可预测的,完全取决于线程调度程序。因此,如果你想要一致的行为,那么你必须在列表本身上使用synchronize
(将其包含在synchronized
块中,并将列表作为参数。
查看文档以了解更多详细信息。
此外,查看此答案以了解更多解释。
对同步列表的迭代不是原子的。客户端必须提供锁定。
如果您查看Collections.synchronizedList
的源文档,它将返回的列表用作锁(mutex(。因此,客户端代码可以通过使用返回的列表应用同步块,使迭代成为原子。
从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