Collections.synchronizedList()方法有什么用?它似乎没有同步列表

Ful*_*Guy 20 java collections multithreading synchronization

我正在尝试使用两个线程添加StringArrayList.我想要的是,当一个线程添加值时,另一个线程不应该干扰,所以我使用了该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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的输出是:

true one
true two
true one
true two
true one
true two
true two
true one
true one
true one...
Run Code Online (Sandbox Code Playgroud)

在显式同步块未注释的情况下,我在添加时停止来自其他线程的干扰.一旦线程获得了锁,它就会执行直到它完成.

取消注释synchronized块后的示例输出:

true one
true one
true one
true one
true one
true one
true one
true one...
Run Code Online (Sandbox Code Playgroud)

那么为什么Collections.synchronizedList()不进行同步呢?

jha*_*mon 26

同步列表仅同步此列表的方法.

这意味着当另一个线程当前正在运行此列表中的方法时,线程将无法修改列表.处理方法时对象被锁定.

例如,假设addAll您的列表中有两个线程运行,其中有两个不同的列表(A=A1,A2,A3,B=B1,B2,B3)作为参数.

  • 由于方法是同步的,您可以确定这些列表不会随机合并 A1,B1,A2,A3,B2,B3

  • 您不确定线程​​何时将进程切换到另一个线程,以便您可以获取A1,A2,A3,B1,B2,B3B1,B2,B3,A1,A2,A3.

在您的第一段代码中,两个线程同时运行.并且都试图将add一个元素列入清单.除了add方法上的同步之外,你没有办法阻止一个线程,因此add在将进程交给线程2之前,没有什么能阻止线程1运行多个操作.所以你的输出是完全正常的.

在你的第二段代码(未注释的代码)中,你清楚地说明一个线程在开始循环之前完全锁定了另一个线程中的列表.因此,您确保一个线程将在另一个线程访问列表之前运行完整循环.


Nic*_*tto 11

Collections.synchronizedList() 将同步所有对备份列表的访问,除非迭代时仍需要在同步块中完成,同步List实例作为对象的监视器.

例如,这里是add方法的代码

public boolean add(E e) {
    synchronized (mutex) {return c.add(e);}
}
Run Code Online (Sandbox Code Playgroud)

这保证了对支持列表的串行访问,因此如果你的2个线程同时调用add,一个线程将获取锁,添加其元素并释放锁,然后第二个线程将能够获取锁并添加其元素就是为什么你或者onetwo你的输出.

取消注释同步块时,代码就是

synchronized(o) {
    for(int i=0;i<100;i++){
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,首先可以获取锁的线程o将在释放锁之前执行整个 for循环(除非抛出异常),允许另一个线程执行其synchronized块的内容,这就是100连续时间的原因one或者two然后是100连续的另一个值.


Mo *_*igi 6

这是一个基于原始示例的很酷的小示例,并接受了用于说明目的的答案synchronizedList

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

原始list最终具有未定义的行为,结果如下:

[one, one, one] // wrong!
[one, one, one, null, null, null] // wrong!
[two, two, two] // wrong!
[one, one, one, two, two, two] // correct
Run Code Online (Sandbox Code Playgroud)

而 synchronizedsynList有一个 synchronizedaddAll方法并且总是产生两个正确结果之一:

[one, one, one, two, two, two] // correct
[two, two, two, one, one, one] // correct
Run Code Online (Sandbox Code Playgroud)