【问题标题】:Is it a good practice to use threads within another threads?在另一个线程中使用线程是一个好习惯吗?
【发布时间】:2017-02-20 16:45:52
【问题描述】:

根据标题,看这段代码:

Thread outer = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread inner1 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //some statements and other inner threads
                    }
                });

                Thread inner2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //some statements and other inner threads
                    }
                });

                //some statements and other inner threads
            }
        });

那么,像这样使用多线程是一个好习惯吗?

问候

【问题讨论】:

  • 请尽量坚持每个问题 1 个问题。
  • @Kayaman 对不起...我只是不想发送 3 个帖子。不过,如果它不好,我会编辑它并再发送 2 个帖子。我应该这样做吗?
  • 目前问题有点太大,无法正确回答。
  • @HMD 几乎从来没有“最好”的方式。这完全取决于编码风格、个人偏好和上下文。根据定义,这是否是不好的做法,答案是否定的。我能想到很多这样的代码就可以的例子。我还可以想到很多这样的代码会很糟糕的情况。
  • 在某种程度上,您实际上总是从其他线程创建线程。程序的主要方法在main 线程上调用。从那里开始并运行其他线程。线程不是嵌套的,它们并行运行,无论你从哪里调用它们。

标签: java multithreading


【解决方案1】:

任何编程语言都没有内部线程。线程只是具有run() 方法的对象,具有多个线程可以同时(并发)运行的特性。

在您的示例中,您在线程的run() 方法中创建了两个线程。这不是问题,因为您可以在任何上下文中创建对象。

如果要使用线程,则必须在创建线程后调用outer.start()inner1.start()inner2.start(),否则不会执行。同样,这绝对没问题,因为线程在哪个上下文中启动并不重要。 Java 虚拟机平等对待所有线程。

【讨论】:

    【解决方案2】:

    根据 Q1 “好不好”。这是正确的,但是仅仅为了创建另一个线程而创建威胁并没有多大意义。

    另外,请记住,创建 Thread 对象是相对昂贵的操作。 您的代码中有 //some statements and other inner threads 注释。考虑使用ThreadPoolExecutor 以避免手动创建许多线程。

    【讨论】:

      【解决方案3】:

      我不喜欢这种方式,因为:

      *可读性较差

      *线程 2 会自动选择线程 1 的优先级,除非你不手动设置

      *基于 JVM 的线程调度程序(如时间片、排队...)所以你不知道线程 1 何时会通过可运行状态

      *如果线程 2 依赖于线程 1,您可以使用等待和通知或其他新功能来实现此目标,这样它会更具可读性

      【讨论】:

        【解决方案4】:

        这篇关于并发的Java Code Geeks 文章为您提供了一些建议。我建议您完整阅读它,但这里有两个重要的 sn-ps:

        通常不建议直接使用Thread类的实例来创建和管理线程...

        还有这个:

        在 Java 中创建新线程很容易,但管理它们确实很困难 艰难的。 Java 标准库提供了非常有用的抽象 旨在简化线程的执行器和线程池的形式 管理。

        本质上,在其最简单的实现中,线程池创建和 维护一个线程列表,可以立即使用。 应用程序不是每次都产生新线程,而是借用 池中的一个(或需要的多个)。一旦借用线程 完成它的工作,它被返回到池中,并成为 可以接下一个任务。

        虽然可以直接使用线程池,但 Java 标准 库提供了一个executors 外观,它有一组工厂方法 创建常用的线程池配置。

        线程有很多状态需要管理,文章中也指出:

        • NEW:尚未启动的线程处于此状态。
        • RUNNABLE:在 Java 虚拟机中执行的线程处于此状态。
        • BLOCKED:被阻塞等待监视器锁的线程处于此状态。
        • WAITING:无限期等待另一个线程执行特定操作的线程处于此状态。
        • TIMED_WAITING:无限期等待另一个线程执行特定操作的线程处于此状态。
        • TERMINATED:已退出的线程处于此状态。

        以下是对this question 的回答中的其他注意事项:

        1. Consider non-blocking I/O

          是的,您可以启动任意数量的线程,但这可能是 不是最好的方法。使用非阻塞 API 会更好 这样您就可以开始执行一些外部调用和调用 线程可以立即开始做其他事情而无需等待 套接字/数据库调用回来。然后,当套接字/数据库 调用返回,触发回调以完成该处理。

          非阻塞 I/O 可以提供高得多的 CPU 利用率,因为您是 只需触发呼叫和注册回调,无需尝试 平衡“正确”的并发线程数量,这些线程主要是 反正就是睡觉。

        2. Consider thread hierarchy

        层次结构重要吗?

        您最好使用带有缓存的 ExecutorService 线程池。

        这样您就可以池线程而不是创建批次(即 昂贵的)。 ExecutorServices 还提供了其他很酷的东西,并且使用 使用它们的 Callables / Runnables 可能比测试容易得多 自己处理线程。

        【讨论】:

        • Re, "通常不建议直接使用 Thread 类的实例来创建和管理线程。"这是国际海事组织的坏建议。如果您想要一个长时间运行的专用线程,那么实例化Thread 是获得它的最简单方法。更简单通常 == 更好,但是,种情况,您可能希望使用 ThreadFactory 代替。现在,我们可以争论您是否应该想要一个长期运行的专业线程,但这是一个不同的讨论主题。
        • 在考虑线程生命周期、资源管理、持久性、监控、标准化/易读性等的权衡时,我不知道“它更简单”是否足够重要。Here 是一个有趣的答案单线程与 Executor 库。此外,直接来自 Oracle 的 good arguments 始终首选并发包。
        猜你喜欢
        • 1970-01-01
        • 2014-04-01
        • 2017-01-14
        • 1970-01-01
        • 1970-01-01
        • 2016-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多