我正在尝试使用两个线程将String值添加到中ArrayList。我想要的是,当一个线程正在添加值时,另一个线程不应干涉,因此我使用了该Collections.synchronizedList方法。但是看来,如果我没有在对象上显式同步,则添加操作将以不同步的方式进行。
String
ArrayList
Collections.synchronizedList
没有显式的同步块:
public class SynTest { public static void main(String []args){ final List<String> list=new ArrayList<String>(); final List<String> synList=Collections.synchronizedList(list); final Object o=new Object(); Thread tOne=new Thread(new Runnable(){ @Override public void run() { //synchronized(o){ for(int i=0;i<100;i++){ System.out.println(synList.add("add one"+i)+ " one"); } //} } }); Thread tTwo=new Thread(new Runnable(){ @Override public void run() { //synchronized(o){ for(int i=0;i<100;i++){ System.out.println(synList.add("add two"+i)+" two"); } //} } }); tOne.start(); tTwo.start(); } }
我得到的输出是:
true one true two true one true two true one true two true two true one true one true one...
在未注释显式同步块的情况下,我在添加时停止了来自其他线程的干扰。一旦线程获得了锁,它将一直执行直到完成。
取消注释同步块后的样本输出:
true one true one true one true one true one true one true one true one...
那么为什么Collections.synchronizedList()不进行同步呢?
Collections.synchronizedList()
同步列表仅同步该列表的方法。
这意味着当另一个线程当前正在运行该列表中的方法时,一个线程将无法修改列表。处理方法时对象被锁定。
举例来说,假设addAll您的清单上有两个执行绪,并以2个不同的清单(A=A1,A2,A3和B=B1,B2,B3)作为参数。
addAll
A=A1,A2,A3
B=B1,B2,B3
随着方法的同步,您可以确保这些列表不会像 A1,B1,A2,A3,B2,B3
A1,B1,A2,A3,B2,B3
您无需确定线程何时将进程移交给另一个线程。每个方法调用必须完全运行并返回,然后另一个方法才能运行。因此,您可以获取A1,A2,A3,B1,B2,B3或B1,B2,B3,A1,A2,A3(因为我们不知道哪个线程调用将首先运行)。
A1,A2,A3,B1,B2,B3
B1,B2,B3,A1,A2,A3
在您的第一段代码中,两个线程同时运行。两者都尝试将add元素添加到列表中。除了add方法上的同步之外,您没有任何方法可以阻塞一个线程,因此,没有什么可以阻止线程1 add在将进程移交给线程2之前运行多个操作。因此,您的输出是完全正常的。
add
在第二段代码(未注释的代码)中,您明确声明一个线程在开始循环之前已完全锁定了另一个线程的列表。因此,您确保一个线程在另一个线程可以访问该列表之前将运行完整循环。