java - 是否有必要对仅在删除操作时访问的列表进行同步

标签 java multithreading linked-list

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

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

最佳答案

您正在从多个线程访问和修改一个-threadsafe 类LinkedList。不,这不安全。它可能在你的测试中对你有用,它可能在 99.9999% 的情况下都有效,但它仍然不安全。

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

Testing thread safety can't be done reliably by running code multiple times to see whether it works.

¹ 更不用说本例中 CountDownLatchFuture.get() 的明显内存效应。然而,代码以相当复杂的方式编写(尤其是对于演示),以对实际运行代码的线程安全性做出任何声明。

关于java - 是否有必要对仅在删除操作时访问的列表进行同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49550119/

相关文章:

Java:如何将二进制字符串转换为图像?

java - 范围 : Add Class name to Test Success

c - 将工作分给更多的线程需要更多的时间,为什么?

java - 从字符列表中创建字符串然后清除它的最有效方法

c - C语言逐行读取文件

java - 使用 java 了解您电脑上的 MS-Office 版本

java - 如何使用 IntelliJ IDEA 14 开发使用 Maven 和 Tycho 构建的 OSGi 应用程序?

带有消费者线程和作业队列的 Ruby Sinatra

objective-c - 如何使用 BOOL 变量同步两个线程?

c - 如何创建一个指向另一个链表的链表?