当前位置: 首页 > 面试题库 >

Java线程创建开销

黎阳冰
2023-03-14
问题内容

传统观点告诉我们,大量的企业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 Client 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个线程,即 线程创建开销为0.1毫秒
。因此,在这样的机器上,每秒几百个新线程造成的开销可以忽略不计(也可以通过比较2线程和100线程列中的数字来看出)。



 类似资料:
  • 本文向大家介绍java 线程创建多线程详解,包括了java 线程创建多线程详解的使用技巧和注意事项,需要的朋友参考一下 Java 线程类也是一个 object 类,它的实例都继承自 java.lang.Thread 或其子类。 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: Tread thread = new Thread(); thread.sta

  • 问题内容: 我是Java技术的新手。我知道在Java中只有两种创建方式 扩展线程类 实施可运行接口 因此,这只是两种创建方法。但是,当我们使用主JVM启动程序时,它启动了一个main 。我认为甚至JVM也必须遵循创建主要方法的规则,以创建主线程JVM必须扩展Thread类或实现。 我尽了最大的努力,但是不知道JVM是如何创建这个主要对象的。当我完全遍历主类()时,我知道这是负责主线程的类。但是在G

  • 问题内容: 我是线程新手。我想创建一些与主线程分开工作的简单函数。但这似乎不起作用。我只想创建一个新线程,并在那里独立于主线程发生的事情做一些事情。这段代码看起来很怪异,但到目前为止,我对线程的了解还很少。你能解释一下这是怎么回事吗? 问题答案: 您正在线程方法中调用该方法。但是只有在线程已经启动时才调用该方法。改为这样做:

  • 主要内容:1 创建线程的两种方式,2 Thread类,3 Runnable接口,4 如何启动线程,5 创建线程的例子1 创建线程的两种方式 有两种创建线程的方法: 通过继承Thread类 通过实现Runnable接口。 2 Thread类 Thread类提供了构造方法和方法来创建和执行线程。Thread类继承Object类并实现了Runnable接口。 Thread类的构造方法有: Thread() Thread(String name) Thread(Runnable r) Thread(Ru

  • 线程的创建 接下来,我们的第一个目标就是创建一个线程并且让他运行起来。一个线程要开始运行,需要这些准备工作: 建立页表映射,需要包括以下映射空间: 线程所执行的一段指令 线程执行栈 操作系统的部分内存空间 设置起始执行的地址 初始化各种寄存器,比如 sp 可选:设置一些执行参数(例如 argc 和 argv等 ) 思考:为什么线程即便与操作系统无关,也需要在内存中映射操作系统的内存空间呢?Clic

  • 问题内容: 由于这个错误,我已经阅读,测试并在墙上摔了头一天。 我在名为这样的类中有一些Java代码,如下所示 我一直在调整JVM设置(从1到15G的任何地方)和(从104k到512M的任何地方)。服务器具有24 GB的RAM,但还必须运行支持该程序的数据库。 创建2-20个线程之后(程序中其他地方也有几十个线程),我得到了错误 产量: 发生这种情况时,系统上总是有大量的可用内存,其他程序继续可以