【问题标题】:Two threads reading same list but from different ends两个线程读取相同的列表但来自不同的端
【发布时间】:2017-12-16 15:18:43
【问题描述】:

我有一个列表,我需要通过两个线程遍历列表。一种从上到下阅读,另一种从下到上阅读。当它们相交时,阅读应该停止。

为了遍历列表,我可以使用 ListIterator,但我无法思考这些线程将如何从同一个列表中读取?

【问题讨论】:

  • how to read from same list 是什么意思?将列表的引用传递给两个线程?!
  • 线程是否编辑列表?停止必须有多精确?例如,如果线程在相交后读取了更多项目,那会很糟糕吗?
  • @ThijsSteel,没有线程仅从列表中读取。是的,相交后阅读项目是不好的。所以目标是读取所有列表,但从不同的端读取而不相交。

标签: java multithreading listiterator


【解决方案1】:

如果考虑到线程 A 处理的总项目数和线程 B 处理的总项目数最终将等于列表中的项目数,有一个简单的方法可以解决此问题。

因此,如果您有一个计数器在每次线程想要处理下一个元素时递减,那么一旦计数器达到零,您就知道您已经完成了。

public class MyThreadSyncer {
  protected final AtomicInteger elementCount;

  public MyThreadSyncer(final Collection c) {
    elementCount = new AtomicInteger(c.size());
  }

  public boolean canProcessNext() {
    return (this.elementCount.decrementAndGet() >= 0);
  }
}

使用这个类,每个线程设置自己的私有计数器(线程 A 为 0,线程 B 为 size-1),然后像这样检查:

if (this.threadSyncer.canProcessNext()) {
  this.theTasks[this.myPosition].process();

  this.myPosition += 1;  // this line in thread A
  this.myPosition -= 1;  // this line in thread B
}

【讨论】:

    【解决方案2】:

    你可以对列表进行分区,一个线程将从 0 读取到一半,第二个线程将读取 half+1 到最后。

    class PrintList implements Runnable{
    
        private List list = new ArrayList();
        public PrintList(List list){
            this.list = list;
        }
    
    
        @Override
        public void run() {
         if(Thread.currentThread().getName() != null && Thread.currentThread().getName().equalsIgnoreCase("thread1")){
             int mid =list.size()/2;
             for(int i = 0; i< mid;i++){
                // System.out.println("Thread 1 "+list.get(i));
             }
         }else if(Thread.currentThread().getName() != null && Thread.currentThread().getName().equalsIgnoreCase("thread2")){
    
             for(int i = mid; i<list.size(); i++){
                 //System.out.println("Thread 2 "+list.get(i));
             }
         }  
        }   
    }
    

    【讨论】:

    • 要求不是对列表进行分区,而是通过两个线程同时读取。 T1 从顶部和 T2 从底部。列表按降序排序,因此 T1 正在读取大文件,而 T2 正在读取较小的文件。
    【解决方案3】:

    由于线程只是读取,因此无需使用列表的线程安全版本。

    为了确保线程在它们相交时停止读取,您需要同步它们,因此在读取另一个项目之前,他们应该检查该项目是否可用。实现这一点的一种天真的方法是将当前索引存储在每个线程中,并让其他线程可以访问该索引(确保同步该索引)。不过,这会导致大量开销。

    一个更好的主意是分批工作。将列表划分为例如 16 个项目的部分。然后线程可以在需要检查交集之前读取整个批次。

    【讨论】:

    • +1 for "batching" @Rogger296,假设您使用两个线程的原因是为了加快计算速度,线程在同步点之间可以做的工作越多,您将获得的好处就越多从使用多个线程。初学者常常会沮丧地发现,设计不佳的多线程计算(线程之间过多的耦合)可能比单线程版本
    • 我在这里使用的列表将文件大小作为属性之一。并根据哪个 List 降序排序。因此,从顶部读取的线程 T1 移动缓慢,因为它必须满足繁重的文件,而线程 T2 正在快速移动,因为它正在从文件大小非常低的列表底部读取。我不能使用批处理,因为它会加载带有大量文件的 Thread T1。所以要求是每次线程从列表中获取单个元素。
    • @Rogger296 您可以让线程一以批量大小 1 或 2 工作,而让线程一以更大的批量大小工作。您甚至可以动态调整批量大小
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多