创建多线程对象比在单个线程中慢

时间:2013-09-13 12:47:09

标签: java multithreading

我有可能是一个基本问题。当我创建1亿个Hashtables时,如果我在一个核心上执行它,我的机器上大约需要6秒钟(运行时间=每个核心6秒)。如果我在12个内核上执行多线程(我的机器有6个内核允许超线程),则需要大约10秒钟(运行时间=每个内核112秒)。

这是我使用的代码:

主要

public class Tests 
{
public static void main(String args[])
{
    double start = System.currentTimeMillis();
    int nThreads = 12;
    double[] runTime = new double[nThreads];

    TestsThread[] threads = new TestsThread[nThreads];
    int totalJob = 100000000;
    int jobsize = totalJob/nThreads;
    for(int i = 0; i < threads.length; i++)
    {
        threads[i] = new TestsThread(jobsize,runTime, i);
        threads[i].start();
    }
    waitThreads(threads);
    for(int i = 0; i < runTime.length; i++)
    {
        System.out.println("Runtime thread:" + i + " = " + (runTime[i]/1000000) + "ms");
    }
    double end = System.currentTimeMillis();
    System.out.println("Total runtime = " + (end-start) + " ms");
}

private static void waitThreads(TestsThread[] threads) 
{
    for(int i = 0; i < threads.length; i++)
    {
        while(threads[i].finished == false)//keep waiting untill the thread is done
        {
            //System.out.println("waiting on thread:" + i);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }   
}
}

import java.util.HashMap;
import java.util.Map;

public class TestsThread extends Thread
{
int jobSize = 0;
double[] runTime;
boolean finished;
int threadNumber;

TestsThread(int job, double[] runTime, int threadNumber)
{
    this.finished = false;
    this.jobSize = job;
    this.runTime = runTime;
    this.threadNumber = threadNumber;
}

public void run()
{
    double start = System.nanoTime();
    for(int l = 0; l < jobSize ; l++)
    {   
         double[] test = new double[65];
    }
    double end = System.nanoTime();
    double difference = end-start;
    runTime[threadNumber] += difference;
    this.finished = true;
}
}

我不明白为什么在多个线程中同时创建对象需要花费更长的时间,然后在1个线程中连续执行。如果我删除我创建Hashtable的行,这个问题就会消失。如果有人能帮助我,我将非常感激。

3 个答案:

答案 0 :(得分:1)

更新:此问题已关联bug report,并已通过Java 1.7u40修复。而Java 1.8从来都不是问题,因为Java 8有一个完全不同的哈希表算法。


由于您没有使用创建的对象,操作将被优化掉。所以你只是在衡量创建线程的开销。这肯定是你开始的线程越多的开销。

我必须更正有关细节的答案,我还不知道:类HashtableHashMap有一些特别之处。它们都在构造函数中调用sun.misc.Hashing.randomHashSeed(this)。换句话说,它们的实例在构造期间逃逸,这对存储器可见性有影响。这意味着它们的构造与ArrayList不同,不能优化,并且多线程构造因该方法内部发生的事情(即同步)而减慢。

如上所述,这对这些类来说很特别,当然还有这个实现(我的设置:1.7.0_13)。对于普通类,这种代码的构造时间直接为零。

在这里,我添加了更复杂的基准代码。观察DO_HASH_MAP = trueDO_HASH_MAP = false之间的区别(当false创建ArrayList而不会产生此类特殊行为时)。

import java.util.*;
import java.util.concurrent.*;

public class AllocBench {
  static final int NUM_THREADS = 1;
  static final int NUM_OBJECTS = 100000000 / NUM_THREADS;
  static final boolean DO_HASH_MAP = true;

  public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService threadPool = Executors.newFixedThreadPool(NUM_THREADS);
    Callable<Long> task=new Callable<Long>() {
      public Long call() {
        return doAllocation(NUM_OBJECTS);
      }
    };

    long startTime=System.nanoTime(), cpuTime=0;
    for(Future<Long> f: threadPool.invokeAll(Collections.nCopies(NUM_THREADS, task))) {
      cpuTime+=f.get();
    }
    long time=System.nanoTime()-startTime;
    System.out.println("Number of threads: "+NUM_THREADS);
    System.out.printf("entire allocation required %.03f s%n", time*1e-9);
    System.out.printf("time x numThreads %.03f s%n", time*1e-9*NUM_THREADS);
    System.out.printf("real accumulated cpu time %.03f s%n", cpuTime*1e-9);

    threadPool.shutdown();
  }

  static long doAllocation(int numObjects) {
    long t0=System.nanoTime();
    for(int i=0; i<numObjects; i++)
      if(DO_HASH_MAP) new HashMap<Object, Object>(); else new ArrayList<Object>();
    return System.nanoTime()-t0;
  }
}

答案 1 :(得分:0)

如果你在6个内核上做什么呢?超线程与拥有两倍内核完全相同,因此您可能也想尝试实际内核的数量。

此外,操作系统不一定会将每个线程安排到自己的核心。

答案 2 :(得分:0)

由于你所做的只是测量时间和搅拌内存,你的瓶颈很可能是你的L3缓存或总线到主内存。在这种情况下,协调线程之间的工作可能会产生如此多的开销,而不是更好。

这对于评论来说太长了,但你的内部循环可能只是

double start = System.nanoTime();
for(int l = 0; l < jobSize ; l++){
    Map<String,Integer> test = new HashMap<String,Integer>();
}
// runtime is an AtomicLong for thread safety
runtime.addAndGet(System.nanoTime() - start); // time in nano-seconds.

花时间可以创建一个HashMap,因此如果你经常调用计时器,你可能无法测量你的想法。

BTW Hashtable是同步的,您可能会发现使用HashMap更快,并且可能更具可扩展性。