【问题标题】:Finding element using multithreading takes double the time of a single thread使用多线程查找元素花费的时间是单线程的两倍
【发布时间】:2021-07-31 01:58:08
【问题描述】:

我正在尝试类似于Search for an element in array with threads 的东西。目标值将在 0Integer.MAX_VALUE * 10L 之间,而不是使用实际数组,目标是通过使用简单的 for 循环找到该目标。

  • Searcher 类将是线程本身。注意 for 循环的条件是 i <= endIndex && Starter.running.get()
  • Starter 将创建线程并负责初始化其他所有内容。

有一个名为running 的变量类型AtomicBoolean 用于确保线程在其中一个找到元素时停止执行。问题在于,与单线程方法相比,多线程方法花费的时间是单线程方法的两倍。如果没有running,多线程代码会花费更长的时间。

我的目标是在找到目标时停止所有线程并知道是否找到目标。当然,如果目标不在范围内,那么线程将不得不检查整个段并且找不到任何东西。

示例输出:

Target is: 15794437641
Multithreaded option has now started...
Found target using multithreading: 15794437641
Found!
Multithreaded time: 00h:00m:08s.714
Multithreaded option has now completed.

Single thread option has now started...
Found target using single thread: 15794437641
Single thread time: 00h:00m:04s.226
Single thread option has now completed...

整个代码:

import java.util.concurrent.atomic.AtomicBoolean;

class Searcher implements Runnable {
    private long target;
    private long startIndex;
    private long endIndex;
    private long range;

    public Searcher(long target, long startIndex, long endIndex, long range) {
        this.target = target;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.range = range;
    }

    public void run() {
        for (long i = startIndex; i <= endIndex && Starter.running.get(); i++) {
            if (i == target) {
                Starter.running.set(false);
                Starter.isFound.set(true);
                System.out.println("Found target using multithreading: " + i);
                Thread.currentThread().interrupt();
            }
        }
    }
}

public class Starter {
    public static final AtomicBoolean running = new AtomicBoolean(true);
    public static final AtomicBoolean isFound = new AtomicBoolean(false);

    private static void findElementMultithreaded(
            int threadNumber, long target, long range
    ) {
        Thread[] threads = new Thread[threadNumber];//store threads created
        long segment = range / threadNumber;
        //create the threads but don't start them yet
        for (int i = 0; i <= threadNumber - 1; i++) {
            Searcher s;
            Thread t;
            long start = i * range;
            long end = range - 1;
            if (i == threadNumber - 1) {
                s = new Searcher(target, start, end, segment);
            } else {
                s = new Searcher(target, start, start + end, segment);
            }
            t = new Thread(s);
            threads[i] = t;
        }
        //start all threads
        for (Thread t : threads) {
            t.start();
        }
        //join all threads
        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void findElementSingleThread(long target, long size) {
        for (long i = 0; i < size; i++) {
            if (i == target) {
                System.out.println("Found target using single thread: " + i);
                break;
            }
        }
    }

    public static String getLapsedTime(long start, long end) {
        long t = end - start;
        long millis = t % 1000;
        long x = t / 1000;
        long seconds = x % 60;
        x /= 60;
        long minutes = x % 60;
        x /= 60;
        long hours = x % 24;
        return String.format(
                "%02dh:%02dm:%02ds.%03d", hours, minutes, seconds, millis
        );
    }


    public static void main(String[] args) {
        int numberOfThreads = 8;
        long size = Integer.MAX_VALUE * 10L;
        long target = (long) (Math.random() * size);
        System.out.println("Target is: " + target);
        long start;
        long end;

        System.out.println("Multithreaded option has now started...");
        start = System.currentTimeMillis();
        findElementMultithreaded(numberOfThreads, target, size);
        if (isFound.get()) {
            System.out.println("Found!");
        }
        end = System.currentTimeMillis();
        System.out.println("Multithreaded time: " + getLapsedTime(start, end));
        System.out.println("Multithreaded option has now completed.\n");
        //
        System.out.println("Single thread option has now started...");
        start = System.currentTimeMillis();
        findElementSingleThread(target, size);
        end = System.currentTimeMillis();
        System.out.println("Single thread time: " + getLapsedTime(start, end));
        System.out.println("Single thread option has now completed...");
    }
}

【问题讨论】:

  • 您包括线程创建和终止的时间;这可能很重要。
  • 正在共享的运行变量是 volatile 吗? stackoverflow.com/questions/13582395/…
  • @iggy,即使我只在启动线程后对代码进行计时,它仍然给出相同的时间。
  • @MauricioGraciaGutierrez 似乎没有任何改变。
  • @M.AlJumaily 我提供了一个按预期工作的答案(恕我直言)

标签: java multithreading


【解决方案1】:

您在线程之间划分数字范围,每个线程获得的工作量。 那么线程 i 的开始和结束索引应该是:

long start = i * segment;
long end = start + segment - 1;

【讨论】:

    【解决方案2】:

    正如 Ella 指出的,线程的开始和结束范围计算不正确。但是,多线程解决方案仍然有效,因为至少有一个线程覆盖了结果段。

    缓慢的最可能原因来自循环比较中的差异。与单线程解决方案的简单比较相比,在每次迭代中添加以下内容会大大增加成本,尤其是因为它是原子的,这意味着锁定和争用:

    Starter.running.get()
    

    【讨论】:

      【解决方案3】:

      这是我在下面描述的修复后的结果

      这是我为使其正常工作所做的工作

      • 以正确的方式划分要搜索的句段
      • 由于 range 未在 Searcher 中使用,我已将其删除
      • 简化 Searcher 类,只保留它需要保留的部分
      • 避免使用runningfound,因为它们代表相同的概念(找到了一个元素)
      • 我没有创建、运行和加入线程数组,而是在创建线程时运行它们
      • 在方法内正确测量时间

      这是代码

      public class Searcher implements Runnable {
          private long threadNumber ;
          private long startIndex;
          private long endIndex;
      
          public Searcher(long threadNumber, long startIndex, long endIndex) {
              this.threadNumber = threadNumber ;
              this.startIndex = startIndex;
              this.endIndex = endIndex;
          }
      
          public void run() {
              long i = startIndex; 
              
              while(i <= endIndex && !Starter.isFound.get()) {
                  if (i == Starter.target) {
                      Starter.isFound.set(true) ;
                      Starter.threadThatFoundIt = this.threadNumber ;
                  }
                  else {
                      i++ ;
                  }
              }
      
              if(Starter.isFound.get()) {
                  Thread.currentThread().interrupt();
              }
          }
      }
      
      import java.util.concurrent.atomic.AtomicBoolean;
      
      public class Starter {
          public static final AtomicBoolean isFound = new AtomicBoolean(false);
          public static long target;
          public static long threadThatFoundIt ;
      
          private static void findElementMultithreaded(
                  int numberOfThreads, long range
          ) {
              long segment = range / numberOfThreads;
      
              System.out.println("Multithreaded option has now started...");
              long start = System.currentTimeMillis();
      
              //create the threads and start them 
              for (int i = 0; i < numberOfThreads ; i++) {
                  Searcher s;
                  Thread t;
      
                  s = new Searcher(i, i*segment, (i+1)*segment) ;
                  t = new Thread(s);
                  t.start(); 
              }
      
              while(!isFound.get()) ; //this could be improved from pooling to firing event or using the join correclty 
              if (isFound.get()) {
                  System.out.println(String.format("Thread %d found target %d using multithreading", Starter.threadThatFoundIt, Starter.target)); 
              }
              long end = System.currentTimeMillis();
              System.out.println("Multithreaded time: " + getLapsedTime(start, end));
              System.out.println("Multithreaded option has now completed.\n");
          }
      
          private static void findElementSingleThread(long size) {
              System.out.println("Single thread option has now started...");
              long start = System.currentTimeMillis();
      
              for (long i = 0; i < size; i++) {
                  if (i == target) {
                      System.out.println("Found target using single thread: " + i);
                      Starter.isFound.set(true) ;
                      break;
                  }
              }
      
              long end = System.currentTimeMillis();
              System.out.println("Single thread time: " + getLapsedTime(start, end));
              System.out.println("Single thread option has now completed...");
          }
      
          public static String getLapsedTime(long start, long end) {
              long t = end - start;
              long millis = t % 1000;
              long x = t / 1000;
              long seconds = x % 60;
              x /= 60;
              long minutes = x % 60;
              x /= 60;
              long hours = x % 24;
              return String.format(
                      "%02dh:%02dm:%02ds.%03d", hours, minutes, seconds, millis
              );
          }
      
      
          public static void main(String[] args) {
              int numberOfThreads = 4;
              long size = Integer.MAX_VALUE * 10L;
              Starter.target = (long) (Math.random() * size);
              System.out.println(String.format("Target is: %d using %d threads", target, numberOfThreads));
              
              findElementSingleThread(size);
      
              findElementMultithreaded(numberOfThreads, size);
          }
      }
      
      

      【讨论】:

        猜你喜欢
        • 2018-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-23
        • 2014-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多