【问题标题】:understanding synchronizedlist in java理解java中的同步列表
【发布时间】:2019-05-25 12:16:28
【问题描述】:

我有一个由一个线程修改并由另一个线程读取的 ArrayList。 阅读线程很乐意阅读修改后列表中剩余的任何内容。当从列表中删除所有项目时,它应该停止。 在 Collections.synchronizedList() 的 java doc 中提到,我们应该在对列表执行任何操作之前手动同步列表。

用户必须手动同步返回的 迭代时列出:

列表列表 = Collections.synchronizedList(new ArrayList()); ...同步(列表){ 迭代器 i = list.iterator(); // 必须在同步块中 而 (i.hasNext()) 富(i.next()); } 不遵循此建议可能会导致不确定的行为。

但是在同步时,它会一直读取直到数据结束,然后才给其他线程修改列表的机会。 (是的,因为列表已锁定)。

package test.thread.concurrency.synchronizedlist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Solution {
     private List<String> list = Collections.synchronizedList(new ArrayList<String>());
    //private List<String> list = new ArrayList<>();
    // private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

    public Solution() {
        for (int i = 0; i < 30; i++) {
            list.add("Item_" + i);
        }
    }

    private void removeItemsFromList() {
        synchronized (list) {
            System.out.println("Removing: " + list.remove(0));
        }
    }

    private void readFromLIst() {

        synchronized (list) {
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                System.out.println("READ: "+ it.next());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        final Solution issue = new Solution();

        Thread t1 = new Thread(() -> {
            issue.readFromLIst();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                issue.removeItemsFromList();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        t2.start();
        t1.start();


        t1.join();
        t2.join();

        System.out.println("Final List: " + issue.list);

    }

}

删除:Item_0 阅读:Item_1 阅读:Item_2 阅读:Item_3 阅读:Item_4 阅读:Item_5 阅读:Item_6 阅读:Item_7 阅读:Item_8 阅读:Item_9 阅读: Item_10 阅读:Item_11 阅读:Item_12 阅读:Item_13 阅读:Item_14 阅读: Item_15 阅读:Item_16 阅读:Item_17 阅读:Item_18 阅读:Item_19 阅读: Item_20 阅读:Item_21 阅读:Item_22 阅读:Item_23​​ 阅读:Item_24 阅读: Item_25 阅读:Item_26 阅读:Item_27 阅读:Item_28 阅读:Item_29 移除:Item_1 移除:Item_2 移除:Item_3 移除:Item_4 移除:Item_5 移除:Item_6 移除:Item_7 移除:Item_8 删除:Item_9 删除:Item_10 删除:Item_11 删除:Item_12 移除:Item_13 移除:Item_14 移除:Item_15 移除: Item_16 移除:Item_17 移除:Item_18 移除:Item_19 移除:Item_20 移除:Item_21 移除:Item_22 移除: Item_23​​ 移除:Item_24 移除:Item_25 移除:Item_26 删除:Item_27 删除:Item_28 删除:Item_29 最终列表:[]

我使用 CopyOnWriteArrayList(没有 synchronized 块)让它工作,但每当修改列表时,它似乎都会产生创建新列表的开销。 p>

在使用 sychronizedList 获得与 CopyOnWriteArrayList 相同的结果时,我是否遗漏了什么?

【问题讨论】:

  • 一个线程从列表中删除项目,另一个线程继续阅读,直到它为空。这是基于现实生活场景吗?你想用这个设计解决什么问题?
  • 似乎生产者-消费者,如果是这样,那么也许列表不是你想要使用的
  • 也许你想使用阻塞队列,BlockingQueue&lt;String&gt; blockingQueue = new LinkedBlockingDeque&lt;&gt;();
  • 我正在尝试它。但是假设一个游戏角色的背包里有 30 个金币。当他花费这些时,同样应该反映在记分板上。
  • 现在看来背包物品应该是一个 bound 属性,允许监听器收到状态更改通知,例如使用属性更改监听器。

标签: java multithreading synchronized java-threads


【解决方案1】:

对于synchronizedList,您必须在遍历它时持有锁。

但是由于您只是在列表上执行删除优先和遍历,因此您可以考虑使用ConcurrentLinkedDeque 代替。来自ConcurrentLinkedDeque的文档:

迭代器是弱一致的,返回的元素反映了在迭代器创建时或之后的某个时间点的双端队列状态。它们不会抛出 ConcurrentModificationException,并且可以与其他操作同时进行。

因此,您可以在其他线程修改它时不加锁地遍历双端队列。

import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;

public class Solution {
    private Deque<String> list = new ConcurrentLinkedDeque<>();

    public Solution() {
        for (int i = 0; i < 30; i++) {
            list.add("Item_" + i);
        }
    }

    private void removeItemsFromList() {
        String item = list.removeFirst();
        System.out.println("REMOVE " + item);
    }

    private void readFromLIst() {
        boolean isEmpty;
        do {
            isEmpty = true;
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                System.out.println("READ " + it.next());
                isEmpty = false;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } while (!isEmpty);

    }

    public static void main(String[] args) throws InterruptedException {
        final Solution issue = new Solution();

        Thread t1 = new Thread(issue::readFromLIst);

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                issue.removeItemsFromList();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        t2.start();
        t1.start();


        t1.join();
        t2.join();

        System.out.println("Final List: " + issue.list);

    }

}

输出:

READ Item_1
REMOVE Item_0
READ Item_2
REMOVE Item_1
READ Item_3
REMOVE Item_2
READ Item_4
REMOVE Item_3
READ Item_5
REMOVE Item_4
REMOVE Item_5
READ Item_6
READ Item_7
REMOVE Item_6
READ Item_8
REMOVE Item_7
READ Item_9
REMOVE Item_8
READ Item_10
REMOVE Item_9
READ Item_11
REMOVE Item_10
READ Item_12
REMOVE Item_11
READ Item_13
REMOVE Item_12
READ Item_14
REMOVE Item_13
READ Item_15
REMOVE Item_14
READ Item_16
REMOVE Item_15
READ Item_17
REMOVE Item_16
READ Item_18
REMOVE Item_17
READ Item_19
REMOVE Item_18
READ Item_20
REMOVE Item_19
REMOVE Item_20
READ Item_21
READ Item_22
REMOVE Item_21
REMOVE Item_22
READ Item_23
READ Item_24
REMOVE Item_23
REMOVE Item_24
READ Item_25
REMOVE Item_25
READ Item_26
READ Item_27
REMOVE Item_26
READ Item_28
REMOVE Item_27
READ Item_29
REMOVE Item_28
READ Item_29
REMOVE Item_29
Final List: []

【讨论】:

  • 我将remove操作的休眠时间减少到50ms。但它似乎正在读取已经从列表中删除的数据。删除 Item_0 阅读:Item_1 删除 Item_1 删除 Item_2 阅读:Item_2 删除 Item_3 删除 Item_4 阅读:Item_3
  • 删除 Item_2 阅读:Item_2
  • 是的,这就是weakly consistent 的意思。
  • 好的。那么我们该如何处理呢?我认为这种情况在游戏中很常见。
  • 什么?我认为您对修改后列表中剩余的内容感到满意。你看,只有“之后”的定义对于迭代器来说有点模糊。我们正在处理一个经典的 X-Y 问题。我建议你不要再担心并发列表/队列了。考虑使用状态监听器。
猜你喜欢
  • 2013-10-14
  • 2012-07-06
  • 2014-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-15
  • 2021-09-02
  • 2011-11-12
相关资源
最近更新 更多