当前位置: 首页 > 知识库问答 >
问题:

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

幸弘扬
2023-03-14

我正在尝试使用两个线程将字符串值添加到数组列表中。我想要的是,当一个线程添加值时,另一个线程不应该干扰,所以我使用了 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...

通过取消对显式synchronized块的注释,我在添加时停止了来自其他线程的干扰。一旦线程获得了锁,它就一直执行,直到完成。

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

true one
true one
true one
true one
true one
true one
true one
true one...

那么,为什么<code>集合会出现问题呢。synchronizedList()是否不执行同步?

共有3个答案

柯镜
2023-03-14

这是一个基于原始示例和公认答案的很酷的小示例,以显示同步列表的用途。

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

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

[one, one, one] // wrong!
[one, one, one, null, null, null] // wrong!
[two, two, two] // wrong!
[one, one, one, two, two, two] // correct

虽然synchronizedsynList有一个synchronisedaddAll,并且总是产生两个正确结果中的一个:

[one, one, one, two, two, two] // correct
[two, two, two, one, one, one] // correct
姜永贞
2023-03-14

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

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

public boolean add(E e) {
    synchronized (mutex) {return c.add(e);}
}

这保证了对备份列表的串行访问,因此,如果您的两个线程同时调用< code>add,一个线程将获取锁,添加其元素并释放锁,然后第二个线程将能够获取锁并添加其元素,这就是为什么您在输出中交替获得< code>one和< code>two的原因。

当您取消对同步块的注释时,代码将被删除

synchronized(o) {
    for(int i=0;i<100;i++){
        ...
    }
}

在这种情况下,可以首先获取o上的锁的线程将在释放锁之前执行整个for循环(除非抛出异常),允许其他线程执行其同步块的内容,这就是为什么您连续获得100一个两个然后100连续乘以其他值。

司徒炎彬
2023-03-14

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

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

例如,假设两个线程在列表中运行addAll,其中2个不同的列表(A = A1,A2,A3B = B1,B2,B3)作为参数。

>

  • 由于该方法已同步,您可以确保这些列表不会像A1,B1,A2,A3,B2,B3那样随机合并

    您不能决定一个线程何时将进程移交给另一个线程。在另一个方法调用运行之前,每个方法调用都必须完全运行并返回。因此,您可以获得<code>A1、A2、A3、B1、B2、B3</code>或<code>B1、B2、B3、A1、A2、A3</code>(因为我们不知道哪个线程调用将首先运行)。

    在第一段代码中,两个线程同时运行。两者都尝试向列表中添加元素。除了 add 方法上的同步之外,您没有任何方法可以阻止一个线程,因此在将进程移交给线程 2 之前,没有什么可以阻止线程 1 运行多个 add 操作。因此,您的输出是完全正常的。

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

  •  类似资料:
    • 问题内容: 我正在尝试使用两个线程将值添加到中。我想要的是,当一个线程正在添加值时,另一个线程不应干涉,因此我使用了该方法。但是看来,如果我没有在对象上显式同步,则添加操作将以不同步的方式进行。 没有显式的同步块: 我得到的输出是: 在未注释显式同步块的情况下,我在添加时停止了来自其他线程的干扰。一旦线程获得了锁,它将一直执行直到完成。 取消注释同步块后的样本输出: 那么为什么不进行同步呢? 问题

    • 问题内容: 我在dos.oracle.com上找到了这个 公共静态列表syncedList(列表列表) 返回由指定列表支持的同步(线程安全)列表。为了保证串行访问,至关重要的是,对后备列表的所有访问都必须通过返回的列表来完成。当用户遍历返回列表时,必须手动对其进行同步: 我的问题是:如果应该返回已经同步的列表,为什么我必须同步列表以对其进行迭代? 我只是在两个线程中访问列表:一个线程只是添加,另一

    • 问题内容: 可以说我们有以下两个示例代码: 还有这个 所以有人可以告诉我现在有什么区别吗? 问题答案: 这两种不同的方法在 功能上是等效的 。有 可能 是一个非常小的 性能 差异: 在字节码级别,作为方法访问标记中设置的位, 同步方法 公布其同步需求。JVM查找该位标志并进行适当同步。 该 同步块 通过存储在该方法的类文件中的字节码定义的操作序列实现其同步。 因此,同步方法 可能会以 更快的速度执

    • 问题内容: 我刚刚安装并重新安装了IntelliJ。每个Java文件都将变成红色。我检查了JDK;它是1.6。##。在构建工作就好了。 我遇到了通常突出显示的错误。在每个声明上: 无法解析符号SomeEntityBean / Bean 并在每种方法上: 无法解析方法SomeFunction() 如何解决这些错误错误? 问题答案: 即使没有Korgen所描述的外部更改,有时IntelliJ也会自己感

    • 我脑子里有一个问题。我读过静态同步方法锁定类对象,同步方法锁定对象的当前实例。那么锁定类对象是什么意思呢? 有人能在这个话题上帮我吗?

    • 问题内容: Java中的同步方法和同步块有什么区别? 我一直在网上搜索答案,人们似乎对此不太确定:-( 我的看法是,两者之间没有区别,只是synch块的作用域可能更多,因此锁定的时间更少了? 如果在静态方法上使用Lock,则采用什么Lock?班级锁是什么意思? 问题答案: 同步方法将方法接收器用作锁(即,用于非静态方法,而用于静态方法的封闭类)。 blocks将表达式用作锁。 因此,以下两种方法等