2008-10-29 37 views
2

我有一些线程相关的问题,假设下面的代码。请忽略代码可能的低效率,我只对线程部分感兴趣。代码性能比较,线程与非线程

//code without thread use 
public static int getNextPrime(int from) { 
    int nextPrime = from+1; 
    boolean superPrime = false; 
    while(!superPrime) { 
     boolean prime = true; 
     for(int i = 2;i < nextPrime;i++) { 
      if(nextPrime % i == 0) { 
       prime = false; 
      } 
     } 
     if(prime) { 
      superPrime = true; 
     } else { 
      nextPrime++; 
     } 
    } 
    return nextPrime; 
} 

public static void main(String[] args) { 
    int primeStart = 5; 
    ArrayList list = new ArrayList(); 
    for(int i = 0;i < 10000;i++) { 
     list.add(primeStart); 
     primeStart = getNextPrime(primeStart); 
    } 
} 

如果我正在运行这样的代码,它大约需要56秒。但是,如果我有以下代码(作为替代):

public class PrimeRunnable implements Runnable { 

    private int from; 
    private int lastPrime; 

    public PrimeRunnable(int from) { 
     this.from = from; 
    } 

    public boolean isPrime(int number) { 
     for(int i = 2;i < from;i++) { 
      if((number % i) == 0) { 
       return false; 
      } 
     } 
     lastPrime = number; 
     return true; 
    } 

    public int getLastPrime() { 
     return lastPrime; 
    } 

    public void run() { 
     while(!isPrime(++from)) 
      ; 
    } 
} 

public static void main(String[] args) { 
    int primeStart = 5; 
    ArrayList list = new ArrayList(); 
    for(int i = 0;i < 10000;i++) { 
    PrimeRunnable pr = new PrimeRunnable(primeStart); 
    Thread t = new Thread(pr); 
    t.start(); 
    t.join(); 
    primeStart = pr.getLastPrime(); 
    list.add(primeStart); 
    } 
} 

整个操作大约需要7秒。我几乎可以肯定的是,即使我一次只创建一个线程,但创建另一个线程时线程并不总是完成。是对的吗?我也很好奇:为什么这个行动结束得这么快?

当我加入一个线程时,其他线程是否继续在后台运行,或者是连接的线程是唯一正在运行的线程?

回答

3

通过将join()方法的循环,你启动一个线程,然后等待该线程在运行下一个之前停止。我想你也许想要更多的东西是这样的:

public static void main(String[] args) { 
    int primeStart = 5; 

    // Make thread-safe list for adding results to 
    List list = Collections.synchronizedList(new ArrayList()); 

    // Pull thread pool count out into a value so you can easily change it 
    int threadCount = 10000; 
    Thread[] threads = new Thread[threadCount]; 

    // Start all threads 
    for(int i = 0;i < threadCount;i++) { 
    // Pass list to each Runnable here 
    // Also, I added +i here as I think the intention is 
    // to test 10000 possible numbers>5 for primeness - 
    // was testing 5 in all loops 
    PrimeRunnable pr = new PrimeRunnable(primeStart+i, list); 
    Thread[i] threads = new Thread(pr); 
    threads[i].start(); // thread is now running in parallel 
    } 

    // All threads now running in parallel 

    // Then wait for all threads to complete 
    for(int i=0; i<threadCount; i++) { 
    threads[i].join(); 
    } 
} 

顺便说pr.getLastPrime()将在没有总理的情况下返回0,所以你可能想将它添加到你的列表之前过滤了这一点。 PrimeRunnable必须吸收添加到最终结果列表中的工作。另外,我认为PrimeRunnable实际上已被打破,仍然有递增的代码。我认为这是固定的,但我实际上并没有编译这个。

public class PrimeRunnable implements Runnable {  
    private int from; 
    private List results; // shared but thread-safe 

    public PrimeRunnable(int from, List results) { 
     this.from = from; 
     this.results = results; 
    } 

    public void isPrime(int number) { 
     for(int i = 2;i < from;i++) { 
       if((number % i) == 0) { 
         return; 
       } 
     } 
     // found prime, add to shared results 
     this.results.add(number); 
    } 

    public void run() { 
     isPrime(from);  // don't increment, just check one number 
    }  
} 

并行运行10000个线程不是一个好主意。创建一个合理大小的固定线程池并让他们从共享队列中提取工作是一个好主意。基本上每个工作人员都从同一队列中抽取任务,对其进行处理并将结果保存在某个地方。与Java 5 +的最接近的端口是使用由线程池支持的ExecutorService。您还可以使用将ExecutorService与结果队列组合在一起的CompletionService。

一个ExecutorService版本会是什么样子:

public static void main(String[] args) { 
    int primeStart = 5; 

    // Make thread-safe list for adding results to 
    List list = Collections.synchronizedList(new ArrayList()); 

    int threadCount = 16; // Experiment with this to find best on your machine 
    ExecutorService exec = Executors.newFixedThreadPool(threadCount); 

    int workCount = 10000; // See how # of work is now separate from # of threads? 
    for(int i = 0;i < workCount;i++) { 
    // submit work to the svc for execution across the thread pool 
    exec.execute(new PrimeRunnable(primeStart+i, list)); 
    } 

    // Wait for all tasks to be done or timeout to go off 
    exec.awaitTermination(1, TimeUnit.DAYS); 
} 

。希望给你一些想法。我希望最后一个例子比第一个更好。

1

仔细阅读您的代码。这两种情况并不是一回事,它与线程无关。

当你加入一个线程时,其他线程将在后台运行,是的。

+0

您能否告诉我您的意思是“这两起案件都没有做同样的事情”? – Geo 2008-10-29 21:20:39

+0

其中一种情况比较慢的原因是它没有做同样的事情。提示:您分别调用isPrime()和getNextPrime()多少次? – JesperE 2008-10-29 21:34:01

0

运行一个测试,第二个似乎不需要9秒钟 - 事实上,它至少需要和第一个一样长(这是可以预料的,threding无法帮助它实现的方式你的榜样

的Thread.join只会返回时thread.joined终止,那么当前线程将继续,你叫加入的人会死

对于一个快速参考。 - 认为穿线时开始一次迭代并不取决于前一次的结果

+0

我在我的笔记本电脑上试了两次。我在帖子中写的时间是我得到的时间。 – Geo 2008-10-29 21:19:29

+0

也许关于我们的虚拟机如何处理不同的东西。可能是因为你的thread.join被破坏了,这将解释运行时的差异......另外,第二个抛出了一个你没有处理的异常。我不知道你是如何运行它的。你在用什么虚拟机? – 2008-10-29 22:28:43

2

您可以通过在您的第一个例子运行线程。用你的主要方法:

private static int currentPrime; 
public static void main(String[] args) throws InterruptedException { 
    for (currentPrime = 0; currentPrime < 10000; currentPrime++) { 
     Thread t = new Thread(new Runnable() { 
      public void run() { 
       getNextPrime(currentPrime); 
      }}); 
     t.run(); 
     t.join(); 
    } 
} 

这将运行在原来的同一时间。

回答你的“加入”问题:是的,当你使用“加入”时,其他线程可以在后台运行,但在这种情况下,一次只能有一个活动线程,因为你阻止在最后一个线程完成执行之前创建新线程。

2

JesperE是正确的,但我并不只给提示(至少以外的教室)认为:

注意这个循环中,非线程版本:

for(int i = 2;i < nextPrime;i++) { 
    if(nextPrime % i == 0) { 
    prime = false; 
    } 
} 

至于反对这线程版本:

for(int i = 2;i < from;i++) { 
    if((number % i) == 0) { 
    return false; 
    } 
} 

第一循环将总是完全贯穿,而如果它找到一个除数第二个会提前退出。

你可以做第一个循环也加入了break语句像这样提前退出:

for(int i = 2;i < nextPrime;i++) { 
    if(nextPrime % i == 0) { 
    prime = false; 
    break; 
    } 
}