java

时间:2016-11-16 21:16:15

标签: java multithreading

这是我的第一个专业线程应用程序,对我来说有些让人困惑。

我需要从API获取大量数据,其中我为三个不同的API端点提供id,并且根据哪个返回非null值,我必须解析并处理数据。

我有一个这样的工作版本,但没有使用线程,并且完成时间太长。

主要思想是我正在使用一个id池,并尝试在循环内连接到API。每个循环都在创建一个处理连接和数据解析的新Object。

public void connect(){
    For(String id:idList){
      String url="...."+id;
      String reply=getData(url);
      if(reply!=null){
         //parse data
         parse(reply);
      } 
    }
}
public void parse(String data){
   //....
}

所以我想试试线程,​​以防万一它加速了。
到目前为止,我的尝试似乎正在工作,我可以连接和下载数据,但我还没想出如何限制我创建的线程,以避免溢出或内存不足错误。
根据我的研究,我发现如果已经运行了太多线程,我可以将新创建​​的线程置于休眠状态,如下所示:(来自www.shayanderson.com的例子)

public class Test {
      public static final int MAX_THREADS = 3;
      public static int threads_counter = 0;

      public class MyThread extends Thread {
            String name;
            String says;
            private final int REPEATS = 1;
            private final int DELAY = 200;

            public MyThread(String in_name, String in_says) {
                  this.name = in_name;
                  this.says = in_says;
            }

            public void run() {
                  if(Test.threads_counter >= Test.MAX_THREADS) {
                        try {
                              Thread.sleep(this.DELAY);
                        } catch(Exception e) {
                              Test.addResponse("Thread error");
                        }
                        this.run();
                        return;
                  }

                  Test.threads_counter++;
                  try {
                        for(int i = 0; i < this.REPEATS; ++i) {
                              Test.addResponse(this.name + " says \"" + this.says + "\"");
                              Thread.sleep(this.DELAY);
                        }
                  } catch(Exception e) {
                        Test.addResponse("And error occured for Thread " + this.name);
                  } finally {
                        Test.addResponse("Thread " + this.name + " stopping");
                        Test.threads_counter--;
                  }
            }
      } 

我的尝试看起来像这样:

public void connect(){
    For(String id:idList){
      String url="...."+id;
      ThreadClass thread= new ThreadClas(url);
      Thread t = new Thread(thread);
       t.start();
    }
}
class ThreadClass implements Runnable{
   public final int MAX_THREADS = 10;
   public int threads_counter = 0; 

   public void run() {
       while(this.threads_counter >= this.MAX_THREADS){
          //sleep
        }
       threads_counter++;
       //fetch data and parse
       threads_counter--;
   }
}

但这是创建线程并且只是暂停它,所以我认为它会消耗RAM,就好像它正在运行并且考虑到我有几千个ID,这肯定会给我带来问题。
因此,如果我可以阻止在主类中创建线程,那将更有意义。我怎么能这样做?如果我使用计数器(如上例所示),我不知道何时终止每个线程以开始创建新线程。
另一方面,我已经看到了wait()/notify()方法,但我不确定如何实现它,因为我正在创建新的对象,因此我无法一起通知它们。 我尝试在Concurrency上阅读Oracle的Java教程,但这让我更加困惑。

更新 - (不是)解决方案
我确实设法改变我的类作为线程运行,使用池(这是任何有类似错误的人的正确方法),但我有连接问题(我使用jdbc-mySql连接和Secure Copy / Jcraft连接)这是一个正确设置的麻烦。在尝试连接时,我获得了打开(和/或关闭)连接或具有太多打开连接的连接的异常。我猜这是因为当我编写应用程序时,我打开并关闭方法内部的这些连接,然后转向Thread,导致打开多个打开的连接。现在改变太多了,所以我只想改回单线程方法。

对于我来说,使用面向线程的方法设计我的程序可能是一个教训,如果需要,可以将它作为单个线程使用。

2 个答案:

答案 0 :(得分:2)

  

但这是创建线程而只是暂停它,

实际上它只是在燃烧CPU。

  

所以我认为它会消耗RAM,就好像它正在运行一样

它消耗CPU而不是RAM。

  

考虑到我有几千个ID,这肯定会给我带来麻烦。

一旦你有更多的线程而不是核心,它将导致问题,很可能是4。

  

因此,如果我可以阻止在主类中创建线程,那将更有意义。我怎么能这样做?

我会使用Queue并传递它的工作,如果你想限制活跃的线程数,我也会使用一个线程池,比如一个固定大小的线程池。

  

如果我使用计数器(如上例所示),我不知道何时终止每个线程以开始创建新线程。

你永远不会因为你的计数器没有线程保存而另一个线程可能永远不会看到它更新。

  

另一方面,我已经看到了wait()/ notify()方法,但我不知道如何实现它,因为我正在创建新的对象,因此我无法一起通知它们。

你可以向他们传递一个他们都能看到的对象,但就像我说固定大小的线程池Executor会简单得多。

  

我试着阅读Oracle关于并发的Java教程,但这让我更加困惑。

由于您要处理所有数据并收集结果,我建议您使用parallelStream()。

public void connect(){
    List<String> results = idList.parallelStream()
                                 .map(id -> fetchDataAndParse(id))
                                 .collect(Collectors.toList());
}

答案 1 :(得分:1)

  

我还没弄明白如何限制我创建的主题。

这强烈表明您需要固定大小的线程。见上面的Peter(+1)Lawry的答案。

  

我想试试线程,​​以防它加速。

在任何程序中使用线程基本上有两个原因:

1)在具有多个CPU的计算机上执行并行计算。 (当我说&#34;计算&#34;,它可能是数学,或者它可能几乎是你的程序在没有做太多IO的情况下使用大量CPU时间的任何其他事情。)

2)简化程序的结构,该程序模拟由不同的,独立的(或部分独立的)外部事件源驱动的不同抽象过程。

在单线程事件驱动的程序中,主事件循环必须接收并分类所有不同类型的事件,然后调用处理程序驱动各种进程。必须在处理程序调用之间持久存在的数据结构中显式编码每个进程的状态。了解此类计划中发生的事情可能是一项挑战。

在多线程程序中,每个抽象过程都可以通过自己的线程建模,并且进程的大部分状态可以由线程的程序计数器和调用堆栈隐式表示。与同等的事件驱动程序相比,多线程程序通常更容易读取。但线程有自己的挑战,编写正确的多线程程序本身就具有挑战性。