有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么线程10000次start()调用比10000次run()调用花费更多的时间?

我在线程上做一个hello world,我使用run()调用(这只是一个普通的方法调用)创建了一个简单线程,使用start()调用创建了一个重复线程,该调用生成另一个线程来处理,但是start()调用所花费的时间比run()调用所花费的时间要多,哪些不是线程调用,为什么是这样

开始通话时间:00:00:08:300

    long time = System.currentTimeMillis();

    for (int i = 0; i < 100000; i++) {
        Thread thread = new Thread(new Car());
        thread.setName(Integer.toString(i));
        thread.start();
    }

    long completedIn = System.currentTimeMillis() - time;

    System.out.println(DurationFormatUtils.formatDuration(completedIn,
            "HH:mm:ss:SS"));

运行调用时间:00:00:01:366

    long time = System.currentTimeMillis();

    for (int i = 0; i < 100000; i++) {
        Thread thread = new Thread(new Car());
        thread.setName(Integer.toString(i));
        thread.run();
    }

    long completedIn = System.currentTimeMillis() - time;

    System.out.println(DurationFormatUtils.formatDuration(completedIn,
            "HH:mm:ss:SS"));

共 (5) 个答案

  1. # 1 楼答案

    start方法就是魔法所在。它会生成一个新线程,这需要时间:

    • 创建线程
    • 开线
    • 管理线程生命周期

    最好的解释将在java.lang.Thread的来源中找到

  2. # 2 楼答案

    您不应该直接调用run。你在那里做的是创建一堆Thread对象,但你从来没有真正创建过新线程;您只需在主线程上运行代码(因为您直接调用run

    创建100000个线程在大多数当前计算机上都不会有好的表现(我不考虑价值数百万美元的高端计算机)。一旦您的线程数量超过了您所能支持的CPU容量,您就开始导致上下文切换。因此,如果您有一个四核系统,运行四个以上的线程实际上会降低您的程序速度(模I/O操作,以及CPU将处于空闲状态的情况)

  3. # 3 楼答案

    从评论到公认的回答:“你对此有什么建议吗?”

    是的,不要直接使用线程。从Java5开始,我们就有了java。util。允许简化线程和任务管理的并发框架

    创建线程的成本很高

    甚至在线程运行您想要的任务之前,就必须创建它,这是一个非常长的过程。正是由于这个原因,我们有了线程池的概念

    不是每次要执行并发任务时都创建新线程,而是先创建线程,然后在需要时发送任务

    线程池是第二个优势。当任务完成时,线程不会被销毁,而是保持活动状态以运行下一个任务,因此线程创建成本只在初始化时发生一次

    那么您如何使用这些概念呢

    首先使用线程池创建一个执行器。为了保持简单,我们创建了一个包含100个线程的线程池(因为您希望模拟100个并发调用的负载):

    ExecutorService pool = Executors.newFixedThreadPool(100);
    

    然后你将提交你的任务:

    long time = System.currentTimeMillis();
    for (int i=0; i<100000; i++) {
      pool.execute(new Car());
    }
    

    重要的是,在停止程序之前,您将等待所有任务完成

    pool.shutdown(); //Do no longer accept new tasks.
    pool.awaitTermination(1, TimeUnit.HOURS); //Wait for up to one hour for all tasks to finish.
    long completedIn = System.currentTimeMillis() - time;
    System.out.println(DurationFormatUtils.formatDuration(completedIn,
                "HH:mm:ss:SS"));
    

    在线程代码中,您没有等待线程完成其任务,事实上,您测量的是线程创建时间,而不是任务运行时间

    代码的作用是什么

    遗嘱执行人。execute方法在线程内执行提供的任务。在这里,它使用100个线程中的一个,让它执行任务

    如果有100多个任务,会发生什么

    对于100个线程,您不能运行超过100个并发任务。其他任务将排队,直到一个任务完成,以便有一个线程可以执行它。这是一个很好的方法,可以确保您不会创建太多线程,并且不会发生OutOfMemory或其他讨厌的事情

    应该使用多少线程

    这取决于您希望执行的任务类型

    如果它像一个web服务器,那么您大部分时间都在IO绑定中等待数据库获取数据,然后等待网络发送响应,您的线程大部分时间都在等待。因此,即使是一个CPU也会从十几个甚至几百个线程中受益。即使更多的线程开始减慢整个应用程序的速度,它也允许处理用户请求,而不是让它等待,或者只是拒绝响应

    如果您的任务受CPU限制,那么您可能希望每个CPU核心有一个任务,以便最大限度地利用硬件,但限制上下文切换的开销。使用4核超线程CPU,最多可以执行8个并发线程

    这个回答只是一个简短的介绍。。。您将通过查看java了解更多信息。util。并发包和阅读一些教程

  4. # 4 楼答案

    run方法是线程启动的方法,但它只是Runnable接口的一个简单方法

    线。start()方法将创建一个新线程(执行线程,一个真实线程),该线程将执行线程实例的run()方法

    可以对线程实例调用run()方法,因为有两种方法可以定义执行线程将执行什么

    • 旧方法:扩展线程的run方法。由于某些原因,包括耦合,不建议再使用它

    • 新方法:将Runnable(提供run()方法)传递给线程的构造函数

    请注意,如果您创建了一个具有可运行(目标)的线程,并且还重写了该线程的run()方法,那么您可以理解当前提供了两个要执行的代码,但将执行哪一个?如果查看线程的默认run()实现,很容易找到:

    public void run() {
        if (target != null) {
            target.run();
        }
    }
    

    如果重写该方法,将绕过提供的可运行目标,除非在重写时调用它

    正如我所说,有两种方法可以启动线程。我猜,当他们设计“新方法”时,他们必须将run()方法保留在Thread类中,以防追溯兼容性问题,但是如果他们能够回到过去,他们可能不会让Thread类实现Runnable接口,这样就不会对线程必须如何启动产生太多混淆

  5. # 5 楼答案

    ^{}实际上创建了一个新线程(重操作),而run调用当前线程上thread对象的方法run(简单方法调用-轻操作)

    从关于start的线程文档:

    Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).