【问题标题】:Deleting from a LinkedList only once in multithread在多线程中仅从链接列表中删除一次
【发布时间】:2018-06-27 18:27:19
【问题描述】:

假设我有一个 LinkedList 填充对象,每个对象都存储我需要的相关信息。我有线程访问此列表的第一个元素以检索信息并对其执行一些操作,在操作结束时,我希望从列表中删除第一个元素。如何确保每个元素只调用一次 remove() ?这样每个线程都可以访问列表中相同的下一个元素?

public class Test {
    private static LinkedList<foo> list;

    public static void main(String args[]) {
        list = new LinkedList<>();
        list.add(foo1);
        list.add(foo2);
        list.add(foo3);
        for(int i = 0; i < 5; i++) {
            new Thread(run()).start();
        }
    }

    private static Runnable run() {
        while(true) {
            while(SomeCondition) {
                //Do work.
            }
            list.remove();
        }
    }
}

我尝试为 remove 方法创建一个锁,但这会导致所有线程的每个元素之间出现明显的延迟。有一个好主意吗

LinkedList<LinkedList<food>> mainList;

这样每个线程都有自己的列表来处理吗?然后每个线程都可以这样做来访问他们的元素:

mainList.get(i).element();
mainList.get(i).remove();

编辑: 我没有提到 main() 有它自己的线程,它不断地将更多元素添加到列表中。主要目的是这是一个不断接收请求的服务器,请求被放置在列表中作为“队列”。然后从 main() 分离的其他线程执行操作。请求的消息包含一个字段,让我知道哪个线程将执行什么操作。

编辑 2: 我可能对获得正确答案的要求太模糊了,对不起!这是一个不断接收来自一个客户端的请求的服务器,该列表不断填充请求,为此目的最重要的字段是请求的时间:

for(int i = 0; i < 5; i++) {
    new Thread(run(i)).start();
}

private static Runnable run(int streamNumber) {
    while(true) {
        while(SomeCondition) {
            //Do work.
        }
        list.remove();
    }
}

因此,streamNumber 规定了以下内容: 1. 我要广播到哪个 IP 地址。 2.使用请求的时间和streamNumber,指向我需要使用的有效载荷的指针(来自文件)。

最后,每个线程都会将payload发送出去。所以每个线程都需要相同的信息,但会做出不同的反应。一旦它发送了有效载荷,它将抓住下一个请求的时间并重新做所有事情。

【问题讨论】:

  • 您是否考虑过为每个线程提供其自己的(浅)列表副本?
  • 或者如果线程没有通过从列表中提取的对象交换数据,那么您甚至可以制作一个 deep 副本。
  • 你能详细说明一下吗?我的下一个思路是创建充满列表的列表,然后每个线程都可以处理自己的列表副本
  • 您现在是在问如何表示列表副本吗?我在这里要做的第一件事就是停止依赖类变量。让每个线程的列表成为单独的Runnable 的实例变量对我来说是有意义的,通过该变量定义线程的工作。您的代码在这个一般区域中被破坏了,但是您可以将列表副本传递给获取Runnables 的方法或构造函数。
  • 对不起!我想我应该包括不断添加列表的事实,因此制作深层副本效率不高。

标签: java multithreading linked-list


【解决方案1】:

首先,当多个线程访问列表以进行写操作时,LinkedList 简直是一个糟糕的选择。您可以启动 here 来查找更适合此类需求的 List 接口的其他实现。

正如您自己评论的那样:另一种选择是将您的数据切片到单独的存储桶中,并为每个线程分配一个不同的存储桶。哪种解决方案能给您带来更好的结果取决于您的具体要求。创建存储桶需要前期成本,而线程安全队列每次添加/删除元素时都会花费您。

另一种方法:

  • 使用一个列表,但没有 5 个线程遍历列表
  • 相反,您顺序迭代列表,然后,对于 每个 循环迭代,您获取一个列表条目,然后将 5 个任务对象推送到某个 ExecutorService 实例中。

该服务之前已实例化,您可以将其基于 ThreadPool 或任何其他您想要的(甚至是单线程执行器,这对于单元测试来说非常有用)。这增加了一些开销,因为您必须创建这些任务对象,但它使其他一切变得更加简单。

【讨论】:

  • 我认为 OP 是说 all 线程使用(相同的)第一个元素,然后它应该被删除一次,所有线程都使用第二个,等等。
  • @JohnBollinger 是的。我用不同的方法改写了我的答案。
  • 感谢您的不同想法,我会让您知道结果如何。
【解决方案2】:

我采用的解决方案是创建一个:

ExecutorService threadPool = Executors.newFixedThreadPool(5);
while(true) {
    Request msg = new Request();
    //Set data into msg via DataInputStream
    for(int i = 0; i < 5; i++) {
        threadPool.execute(stream(i, msg));
    }
}

private Runnable stream(int streamNumber, Request msg) {
    //Do some work.
}

我只是将 5 个任务推入池中,而不是让 5 个线程使用 1 个列表并遇到复杂情况。 streamNumber 和消息(标志等)将指示在给定的请求时间内任务会发生什么。感谢大家的意见!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 2017-02-20
    • 2015-11-10
    • 1970-01-01
    相关资源
    最近更新 更多