代码之家  ›  专栏  ›  技术社区  ›  Sikorski

是否有必要对仅用于删除操作的列表进行同步

  •  1
  • Sikorski  · 技术社区  · 6 年前

    我有一个 LinkedList 已使用某些对象初始化。现在,元素将从 head 其中 linkedlist 来自多个线程。只要没有线程获得任何重复的元素,那么哪个线程获得什么元素并不重要。我想知道是否有必要对这个列表进行同步,以便按顺序删除,还是需要使用列表的任何并发变体。仅注释 linkedlist.poll() 方法将从其他线程调用。为了测试它,我还编写了一个测试,其中有一个整数列表,我从多个线程中提取几个整数并将它们相加。然后,当所有线程都完成时,我断言list拥有的整数的总和等于这些线程返回的所有和的总和。对我来说,如果没有同步,它永远不会失败,那么我的假设是正确的还是测试中有错误?

    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.concurrent.*;
    import java.util.function.BiFunction;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    public class Demo {
    
        private final LinkedList<Integer> list;
        private static final int THREAD_COUNT = 5;
        private static final int LIST_SIZE = 250;
    
        public Demo(){
            list = IntStream.rangeClosed(1, LIST_SIZE)
                    .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
        }
    
        private final static BiFunction<Demo, CountDownLatch, Callable<Integer>> callableFactory = (demo, latch) -> () -> {
            try {
                System.out.println("Here "+ Thread.currentThread().getName());
                latch.countDown();
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Running task on thread "+ Thread.currentThread().getName());
            return IntStream.rangeClosed(1, LIST_SIZE / THREAD_COUNT)
                    .map(x -> demo.getNumber())
                    .sum();
        };
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            if(LIST_SIZE < THREAD_COUNT || LIST_SIZE % THREAD_COUNT != 0) throw new IllegalArgumentException("Wrong parameters to test");
            ExecutorService exe = Executors.newFixedThreadPool(THREAD_COUNT);
            CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
            Demo d = new Demo();
            List<Future<Integer>> futures = exe.invokeAll(IntStream.rangeClosed(1, THREAD_COUNT).mapToObj(x-> callableFactory.apply(d, latch)).collect(Collectors.toList()));
            System.out.println("Tasks submitted");
            int sum = 0;
            for(Iterator<Future<Integer>> itr = futures.iterator(); itr.hasNext(); sum+=itr.next().get());
            System.out.println(sum);
            System.out.println(IntStream.rangeClosed(1, LIST_SIZE).sum());
            exe.shutdownNow();
        }
    
        public int getNumber(){
            return list.poll();
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Kayaman    6 年前

    您正在访问和修改 -螺纹安全等级 LinkedList 来自多个线程。不,这不安全。它可能在测试中对您有效,可能在99.9999%的情况下有效,但仍然不安全。

    此类测试的一个常见问题是 System.out.println() 是同步的¹。这可能会导致测试在打印时工作,但在删除测试时失败,并且不会发生影响代码的“意外”同步 正在尝试测试。这并不意味着打印东西可以使代码线程安全,这只是一个潜在的副作用。

    通过运行多个代码无法可靠地测试线程安全性 看看它是否有效。

    更不用说在这个例子中 CountDownLatch Future.get() 。然而,代码是以非常复杂的方式编写的(尤其是对于演示),以声明实际运行代码的线程安全性。