Java 线程创建开销

标签 java performance multithreading

传统智慧告诉我们,大容量企业 Java 应用程序应优先使用线程池,而不是生成新的工作线程。 java.util.concurrent 的使用使这变得简单。

但是,确实存在不适合线程池的情况。我目前正在研究的具体示例是使用 InheritableThreadLocal,它允许将 ThreadLocal 变量“传递”到任何生成的线程。这种机制在使用线程池时会中断,因为工作线程通常不是从请求线程产生的,而是预先存在的。

现在有一些方法可以解决这个问题(可以显式传入线程局部变量),但这并不总是合适或实用的。最简单的解决方案是按需生成新的工作线程,并让 InheritableThreadLocal 完成其工作。

这让我们回到了这个问题——如果我有一个大容量站点,其中用户请求线程每个都产生六个工作线程(即不使用线程池),这会给 JVM 带来问题吗?我们可能会谈论每秒创建几百个新线程,每个线程持续时间不到一秒。现代 JVM 是否对此进行了很好的优化?我记得在 Java 中需要对象池的日子,因为创建对象的成本很高。从那以后,这变得不必要了。我想知道这是否同样适用于线程池。

如果我知道要衡量什么,我会对其进行基准测试,但我担心问题可能比使用分析器衡量的更为微妙。

注意:使用线程局部变量的智慧不是这里的问题,所以请不要建议我不要使用它们。

最佳答案

这是一个微基准测试示例:

public class ThreadSpawningPerformanceTest {
static long test(final int threadCount, final int workAmountPerThread) throws InterruptedException {
    Thread[] tt = new Thread[threadCount];
    final int[] aa = new int[tt.length];
    System.out.print("Creating "+tt.length+" Thread objects... ");
    long t0 = System.nanoTime(), t00 = t0;
    for (int i = 0; i < tt.length; i++) { 
        final int j = i;
        tt[i] = new Thread() {
            public void run() {
                int k = j;
                for (int l = 0; l < workAmountPerThread; l++) {
                    k += k*k+l;
                }
                aa[j] = k;
            }
        };
    }
    System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms.");
    System.out.print("Starting "+tt.length+" threads with "+workAmountPerThread+" steps of work per thread... ");
    t0 = System.nanoTime();
    for (int i = 0; i < tt.length; i++) { 
        tt[i].start();
    }
    System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms.");
    System.out.print("Joining "+tt.length+" threads... ");
    t0 = System.nanoTime();
    for (int i = 0; i < tt.length; i++) { 
        tt[i].join();
    }
    System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms.");
    long totalTime = System.nanoTime()-t00;
    int checkSum = 0; //display checksum in order to give the JVM no chance to optimize out the contents of the run() method and possibly even thread creation
    for (int a : aa) {
        checkSum += a;
    }
    System.out.println("Checksum: "+checkSum);
    System.out.println("Total time: "+totalTime*1E-6+" ms");
    System.out.println();
    return totalTime;
}

public static void main(String[] kr) throws InterruptedException {
    int workAmount = 100000000;
    int[] threadCount = new int[]{1, 2, 10, 100, 1000, 10000, 100000};
    int trialCount = 2;
    long[][] time = new long[threadCount.length][trialCount];
    for (int j = 0; j < trialCount; j++) {
        for (int i = 0; i < threadCount.length; i++) {
            time[i][j] = test(threadCount[i], workAmount/threadCount[i]); 
        }
    }
    System.out.print("Number of threads ");
    for (long t : threadCount) {
        System.out.print("\t"+t);
    }
    System.out.println();
    for (int j = 0; j < trialCount; j++) {
        System.out.print((j+1)+". trial time (ms)");
        for (int i = 0; i < threadCount.length; i++) {
            System.out.print("\t"+Math.round(time[i][j]*1E-6));
        }
        System.out.println();
    }
}
}

在 Intel Core2 Duo E6400 @2.13 GHz 上使用 32 位 Sun 的 Java 1.6.0_21 客户端 VM 的 64 位 Windows 7 上的结果如下:

Number of threads  1    2    10   100  1000 10000 100000
1. trial time (ms) 346  181  179  191  286  1229  11308
2. trial time (ms) 346  181  187  189  281  1224  10651

结论:两个线程的工作速度几乎是一个线程的两倍,正如预期的那样,因为我的计算机有两个内核。我的计算机每秒可以产生近 10000 个线程,即。 e. 线程创建开销为 0.1 毫秒。因此,在这样的机器上,每秒几百个新线程的开销可以忽略不计(通过比较 2 和 100 个线程的列中的数字也可以看出)。

关于Java 线程创建开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2117072/

相关文章:

c# - C# 中的字符串比较性能

mysql - 提高查询性能

c++ - 解包 std::reference_wrapper 的成本

java - Apache Ignite 持久性问题/警告

multithreading - 信号量和上下文切换之间的交互

c++ - 当进程结束时,处于 Sleep() 中间的线程会发生什么?

java - 有没有办法可以在Windows开发盒上开发和测试MapReduce程序

java - 如何在 ListIterator(反向)迭代期间正确添加对象?

java - 如何将 jar 文件转换为 exe 和 rpm

java - 有没有一种方法可以模板化抽象方法,以便抽象方法的参数的类型与实现子类的类型相同?