java - 线程安全地在两个数组列表之间移动数组列表元素

标签 java arraylist concurrency thread-safety

假设我有两个 ArrayList。

A1 = { 1, 2, 3}
A2 = { 4, 5}

我想将元素“4”移动到 A1。我需要一个线程安全的解决方案来做到这一点。例如,以下步骤可能会在给定时间丢失元素“4”。

  1. 从 A2 中删除元素 4

此时,如果另一个线程遍历这两个数组列表的元素,它将看不到元素 4。

  • 将元素 4 插入到 A1
  • 我希望这是一个完整的过程。

    最佳答案

    您绝对应该阅读 thread synchronization .

    主要思想:每次对共享状态(=两个列表)的访问都必须同步。在您的情况下,可以通过使用 synchronized 关键字轻松完成此操作。

    示例:

    public class ThreadSafeObject<T> {
        private final List<T> a1 = new ArrayList<>();
        private final List<T> a2 = new ArrayList<>();
    
        public synchronized void moveFromA1ToA2(int index) {
            T elem = a1.remove(index);
            a2.add(elem);
        }
    
        public synchronized void traverseA1(Consumer<? super T> consumer) {
            a1.forEach(consumer);
        }
    }
    

    正如您所看到的,这两个方法都是同步的,这意味着如果另一个线程已经在执行这两个方法中的任何一个,则线程不得进入这两个方法中的任何一个。

    请注意,仅同步移动方法不够。并且您不得允许直接访问这两个列表。

    有关 synchronized 关键字和内在锁的更多详细信息,请阅读 the documentation mentioned above .

    添加

    由于对此答案的讨论,我决定使用 ReadWriteLock 添加另一个解决方案。此解决方案的优点是线程在遍历列表(= 读访问)时不会互相锁定。

    请注意,主要原则是相同的:我们必须使每次对列表的访问都是线程安全的!更重要的是,因为我们有一个 invariant包括两个列表,无论我们想访问第一个、第二个还是两个列表,我们都必须使用同一个锁。

    尽管如此,只有准确的测量才能表明该解决方案在您的具体情况下是否确实更快(使用锁可能比同步更昂贵)。

    public class ThreadSafeObject<T> {
        private final List<T> a1 = new ArrayList<>();
        private final List<T> a2 = new ArrayList<>();
    
        private final ReadWriteLock lock = new ReentrantReadWriteLock(false);
    
        public void moveFromA1ToA2(int index) {
            lock.writeLock().lock();
            try {
                T elem = a1.remove(index);
                a2.add(elem);
            } finally {
                lock.writeLock().unlock();
            }
        }
    
        public void traverseA1(Consumer<? super T> consumer) {
            lock.readLock().lock();
            try {
                a1.forEach(consumer);
            } finally {
                lock.readLock().unlock();
            }
        }
    }
    

    注意:任何进行更复杂、更巧妙的同步的尝试都可能会失败。锁 strip 化可能是一个可能的改进,但因此我们必须了解有关您的要求的更多详细信息(例如,元素的顺序重要吗?我们可以在中间有 null 项吗?您真的想遍历吗?按特定顺序列出列表,或者仅搜索项目就足够了,等等)。可能不值得付出努力。

    关于java - 线程安全地在两个数组列表之间移动数组列表元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52293062/

    相关文章:

    java - 如何从动态创建的 EditText 获取输入?

    java - 如何根据对象变量值的独特性将对象排序/分组到列表中?

    performance - 缓存高流量站点时处理并发问题

    c# - 为什么 Task.WhenAll 不工作?

    java - java中静态成员的行为

    java - 通过另一个实体进行 Hibernate 映射

    c# - C# 中的 java.lang.Long

    java - 使用 hibernate 工具自动创建序列

    java - 将 ArrayList 传递给 servlet?

    java - Java代码中的Activiti工作流调用