【问题标题】:Synchronize on List [duplicate]在列表上同步 [重复]
【发布时间】:2021-12-06 19:40:41
【问题描述】:

假设我有这个代码块:

List<Integer> lst = Collections.synchronizedCollection(new ArrayList<>());

而我有以下两种方法:

public Integer returnFirst() {
  lst.get(0);
}

public void iterate() {
synchronized(lst) {
     Iterator i = lst.iterator();
     while (i.hasNext()) {
       System.out.println(i);
     }
   }
}

假设一个线程调用iterate(),然后另一个线程调用returnFirst()。由于您在迭代中同步 List 对象,并且迭代当前正在运行,因此 returnFirst() 会被阻止吗?

【问题讨论】:

  • 你的代码能编译吗?
  • 没有。该关键字用于排队任务,除非 returnFirst() 与同一个 object 同步。
  • @Darkman - lst 是内部同步的。

标签: java multithreading synchronization


【解决方案1】:

是的。 看看SynchronizedCollection构造函数和SynchronizedList#get方法:

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }
        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }

可以看到,用于控制并发访问的内部互斥锁其实就是对象本身(mutex = this),也就是说你的iterate()方法中的lst.get(0)synchronized(LST)竞争同一个锁:对象自己。

另外,你的代码不会编译,请将synchronizedCollection替换为synchronizedList

简单代码测试:

    public static void main(String[] args) throws InterruptedException {
        List<Integer> list = Collections.synchronizedList(new ArrayList<>());
        list.add(1);
        new Thread(() -> {
            try {
                synchronized(list) {
                    Thread.sleep(5000);
                    Iterator i = list.iterator();
                    while (i.hasNext()) {
                        System.out.println(i.next());
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
            // It will block for 5 seconds and then output
            System.out.println(list.get(0));
        }).start();
    }

【讨论】:

    【解决方案2】:

    是的,你是对的。 returnFirstiterate 完成之前不会运行。

    【讨论】:

    • 为什么 returnFirst 不会运行呢?它没有同步,所以我认为它没有被阻止。
    • 同步的。 List&lt;Integer&gt; lst = Collections.synchronizedCollection(new ArrayList&lt;&gt;());。它在lst 所指的集合上同步,documentation 中涵盖了这种确切情况,
    • @Mason 这意味着get()lst 相同的对象同步,可以使用synchronized get()synchronized(this) {}lst == this。假设它使用相同的锁是不安全的——开发人员可能会改变这一点,但我对此表示怀疑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多