Java 为什么说创建线程很昂贵?

Java 为什么说创建线程很昂贵?,java,multithreading,performance,concurrency,Java,Multithreading,Performance,Concurrency,Java教程说创建线程很昂贵。但到底为什么这么贵呢?当一个Java线程被创建时,会发生什么事情,使得它的创建变得昂贵?我认为这是真的,但我只对JVM中线程创建的机制感兴趣 线程生命周期开销。线程创建和拆卸不是免费的。实际开销因平台而异,但线程创建需要时间,会给请求处理带来延迟,并且需要JVM和操作系统进行一些处理活动。如果像在大多数服务器应用程序中一样,请求频繁且轻量级,那么为每个请求创建一个新线程可能会消耗大量计算资源 实践中的Java并发 作者:布赖恩·戈茨、蒂姆·佩尔斯、约书亚·布洛赫、约

Java教程说创建线程很昂贵。但到底为什么这么贵呢?当一个Java线程被创建时,会发生什么事情,使得它的创建变得昂贵?我认为这是真的,但我只对JVM中线程创建的机制感兴趣

线程生命周期开销。线程创建和拆卸不是免费的。实际开销因平台而异,但线程创建需要时间,会给请求处理带来延迟,并且需要JVM和操作系统进行一些处理活动。如果像在大多数服务器应用程序中一样,请求频繁且轻量级,那么为每个请求创建一个新线程可能会消耗大量计算资源

实践中的Java并发
作者:布赖恩·戈茨、蒂姆·佩尔斯、约书亚·布洛赫、约瑟夫·鲍比尔、大卫·霍姆斯、道格·利亚
打印ISBN-10:0-321-34960-1

为什么说创建线程很昂贵


因为它在理论上是,这取决于JVM。实际上,每个线程都有相对较大的堆栈内存(我认为每个默认值为256 KB)。此外,线程被实现为操作系统线程,因此创建它们涉及操作系统调用,即上下文切换


请务必意识到,计算中的“昂贵”总是非常相对的。相对于大多数对象的创建而言,线程创建的成本非常高,但相对于随机硬盘搜索而言,成本并不高。您不必不惜一切代价避免创建线程,但每秒创建数百个线程并非明智之举。在大多数情况下,如果您的设计需要大量线程,则应使用有限大小的线程池。

创建
线程
需要分配相当数量的内存,因为它必须创建两个新堆栈(一个用于java代码,一个用于本机代码)。使用/线程池可以避免开销,方法是将线程重新用于的多个任务。

有两种线程:

  • 正确线程:这些是底层操作系统线程设施的抽象。因此,线程创建和系统一样昂贵——总是有开销的

  • “绿色”线程:由JVM创建和调度,这些线程更便宜,但没有适当的并行性。它们的行为类似于线程,但在操作系统的JVM线程中执行。据我所知,它们不常被使用

  • 在线程创建开销中,我能想到的最大因素是为线程定义的堆栈大小。运行VM时,线程堆栈大小可以作为参数传递

    除此之外,线程创建主要依赖于操作系统,甚至依赖于虚拟机实现


    现在,让我指出一点:如果您计划在运行时的每一秒每秒触发2000个线程,那么创建线程的成本很高。JVM不是为处理这个问题而设计的。如果你有一对稳定的工人,他们不会一次又一次地被解雇和杀害,那就放轻松。

    显然,问题的关键是“昂贵”是什么意思

    线程需要创建堆栈并基于run方法初始化堆栈

    它需要设置控制状态结构,即处于可运行、等待等状态


    在设置这些东西时,可能会有大量的同步。

    其他人已经讨论了线程的成本从何而来。这个答案涵盖了为什么创建线程与许多操作相比没有那么昂贵,但与相对便宜的任务执行替代方案相比相对昂贵

    在另一个线程中运行任务最明显的替代方法是在同一个线程中运行任务。对于那些认为线程越多越好的人来说,这很难理解。逻辑是,如果将任务添加到另一个线程的开销大于您节省的时间,那么在当前线程中执行任务可能会更快

    final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
    Runnable task = new Runnable() {
        @Override
        public void run() {
            queue.add(1);
        }
    };
    
    for (int t = 0; t < 3; t++) {
        {
            long start = System.nanoTime();
            int runs = 20000;
            for (int i = 0; i < runs; i++)
                new Thread(task).start();
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in a new Thread %.1f us%n", time / runs / 1000.0);
        }
        {
            int threads = Runtime.getRuntime().availableProcessors();
            ExecutorService es = Executors.newFixedThreadPool(threads);
            long start = System.nanoTime();
            int runs = 200000;
            for (int i = 0; i < runs; i++)
                es.execute(task);
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in a thread pool %.2f us%n", time / runs / 1000.0);
            es.shutdown();
        }
        {
            long start = System.nanoTime();
            int runs = 200000;
            for (int i = 0; i < runs; i++)
                task.run();
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in the same thread %.2f us%n", time / runs / 1000.0);
        }
    }
    }
    
    另一种选择是使用线程池。线程池的效率可能更高,原因有二。1) 它重用已经创建的线程。2) 您可以调整/控制线程数,以确保获得最佳性能

    以下程序打印

    Time for a task to complete in a new Thread 71.3 us
    Time for a task to complete in a thread pool 0.39 us
    Time for a task to complete in the same thread 0.08 us
    Time for a task to complete in a new Thread 65.4 us
    Time for a task to complete in a thread pool 0.37 us
    Time for a task to complete in the same thread 0.08 us
    Time for a task to complete in a new Thread 61.4 us
    Time for a task to complete in a thread pool 0.38 us
    Time for a task to complete in the same thread 0.08 us
    
    这是对一项琐碎任务的测试,该任务暴露了每个线程选项的开销。(此测试任务是在当前线程中实际执行得最好的任务。)

    final BlockingQueue=new LinkedBlockingQueue();
    Runnable任务=新的Runnable(){
    @凌驾
    公开募捐{
    添加(1);
    }
    };
    for(int t=0;t<3;t++){
    {
    长启动=System.nanoTime();
    整数运行=20000;
    for(int i=0;i
    如您所见,创建一个新的
    final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
    Runnable task = new Runnable() {
        @Override
        public void run() {
            queue.add(1);
        }
    };
    
    for (int t = 0; t < 3; t++) {
        {
            long start = System.nanoTime();
            int runs = 20000;
            for (int i = 0; i < runs; i++)
                new Thread(task).start();
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in a new Thread %.1f us%n", time / runs / 1000.0);
        }
        {
            int threads = Runtime.getRuntime().availableProcessors();
            ExecutorService es = Executors.newFixedThreadPool(threads);
            long start = System.nanoTime();
            int runs = 200000;
            for (int i = 0; i < runs; i++)
                es.execute(task);
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in a thread pool %.2f us%n", time / runs / 1000.0);
            es.shutdown();
        }
        {
            long start = System.nanoTime();
            int runs = 200000;
            for (int i = 0; i < runs; i++)
                task.run();
            for (int i = 0; i < runs; i++)
                queue.take();
            long time = System.nanoTime() - start;
            System.out.printf("Time for a task to complete in the same thread %.2f us%n", time / runs / 1000.0);
        }
    }
    }