代码之家  ›  专栏  ›  技术社区  ›  skaffman

Java线程创建开销

  •  45
  • skaffman  · 技术社区  · 14 年前

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

    InheritableThreadLocal ThreadLocal 要“向下传递”到任何衍生线程的变量。这种机制在使用线程池时会中断,因为工作线程通常不是从请求线程派生的,而是预先存在的。

    现在有办法解决这个问题(可以显式地传递线程局部变量),但这并不总是合适或实用的。最简单的解决方案是根据需要生成新的工作线程,并让 可继承的线程本地

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

    4 回复  |  直到 14 年前
        1
  •  37
  •   Jaan    7 年前

    以下是一个微基准的示例:

    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();
        }
    }
    }
    

    在英特尔Core2 Duo E6400@2.13 GHz上安装32位Sun Java 1.6.0_21客户端虚拟机的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
    

    . 因此,在这样的机器上,每秒数百个新线程所带来的开销可以忽略不计(通过比较2个线程和100个线程列中的数字也可以看出这一点)。

        2
  •  9
  •   Michael Borgwardt    14 年前

    一个主要因素是分配给每个线程的堆栈内存,您可以使用 -Xssn

    这只是一个猜测,但我认为“每秒有几百个新线程”肯定超出了JVM设计的舒适处理能力。我怀疑一个简单的基准测试会很快暴露出一些不合适的问题。

        3
  •  1
  •   Bozho Michał Mech    14 年前
    • JMeter +一个分析器,它应该可以让您直接了解在这种重载环境中的行为。只需让它运行一个小时,并监控内存、cpu等。如果没有任何故障,cpu也不会过热,就可以了:)

    • 也许您可以获得一个线程池,或者通过添加一些代码来定制(扩展)您正在使用的线程池,以便获得适当的线程池 InheritableThreadLocal a:It’每次都要定一个时间 Thread 从线程池中获取。 每个 线 具有以下包私有属性:

      /* ThreadLocal values pertaining to this thread. This map is maintained
       * by the ThreadLocal class. */
      ThreadLocal.ThreadLocalMap threadLocals = null;
      
      /*
       * InheritableThreadLocal values pertaining to this thread. This map is
       * maintained by the InheritableThreadLocal class.  
       */ 
      ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
      

      您可以将这些(以及反射)与 Thread.currentThread()

        4
  •  0
  •   Terje    14 年前

    我想知道如果每个用户请求的典型生命周期只有一秒钟那么短,是否有必要在每个用户请求上生成新线程。您是否可以使用某种类型的通知/等待队列,在该队列中生成给定数量的(守护进程)线程,它们都会等待,直到有任务要解决。如果任务队列变长,则会产生额外的线程,但不会以1:1的比例产生。它很可能会比产生数百个生命周期如此短的新线程表现得更好。