在java中实现多线程的最佳方法是什么?

时间:2021-03-13 07:36:45

标签: java multithreading

我一直在关注一些关于在 Java 中实现多线程操作的 YouTube 演示和教程。但是,所有教程都显示了以下过程:

class Task implements Runnable {
    @Override
    public void run() {
        doTask();
    }

    public void doTask() {
        for (int i = 0; i < 1500; i++) {
            System.out.print('T');
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Task t = new Task();

        Thread thread = new Thread(t);
        thread.start();

        for (int i = 0; i < 1500; i++) {
            System.out.print('M');
        }
    }
}

一些演示扩展了 Thread 类而不是 Runnable,但是,它们遵循类似的概念。

然而,我认为的一个问题是,如果我想在一个类中拥有多个逻辑并同时运行,那么我遇到了一个问题。好吧,Java 老手可能知道一些技巧来做到这一点。但是,我尝试以不同的方式实现这种逻辑。这是我写的代码:

class Task {
    public void doTask() {
        for (int i = 0; i < 1500; i++) {
            System.out.print('T');
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Task t = new Task();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                t.doTask();
            }
        });
        thread.start();

        for (int i = 0; i < 1500; i++) {
            System.out.print('M');
        }
    }
}

现在,我可以启动多个线程并调用我想要同时运行的特定方法。

现在,我想知道使用第一种方法实现多线程操作有什么好处,后一种方法有什么缺点吗?

4 个答案:

答案 0 :(得分:3)

使用仅调用 Runnablerun() 方法定义 doTask 类是没有意义的。只需将 doTask 的内容放在 run 中。

或者,最好使用 lambda:

Thread thread = new Thread(() -> {
  // Stuff you want to do.
});

但绝对没有充分的理由扩展 Thread

事实上,在许多情况下,您不想直接使用 Thread:而是使用 ExecutorService,您可以向其提交 Runnable/Callable 实例。

答案 1 :(得分:1)

<块引用>

在java中实现多线程的最佳方式是什么?

没有单一的“最佳”方式。

不同的方式......各有优缺点。

最简单的方法是使用标准的 ExecutorService 实现之一,并将您的任务作为 lambda 表达式或 RunnableCallable 的实例提交。

ForkJoin 池是相似的,但针对不同类型的任务进行了调整,其中一个任务分叉多个子任务然后加入它们;例如分而治之的算法。

另一种方法是使用第 3 方线程池;例如由番石榴提供。

如果你真的需要管理你自己的线程,你可以实现你自己的线程池,但可能有一些东西可以提供你需要的功能。

如果你有一个需要定期运行的任务(在一个线程中),看看TimerTask或更复杂的任务调度库;例如石英。

如果您遇到线程是静态且长时间运行的情况,您可以直接实例化并启动 Thread 对象,将线程主体作为 Runnable(或 lambda)传递。< /p>

几乎唯一避免的事情就是编写一个extendsThread的类。这是一个坏主意。如果您遇到一个示例或演示表明这样做,忽略它。扩展 Thread 会将您的业务逻辑嵌入到线程的 run() 方法中。这使得您很难调整您的代码以使用不同的线程管理策略。

推荐阅读:


最后,有一个正在开发轻量级线程模型的 Java 孵化器项目(“Loom”)。这些线被称为“纤维”。它现在可能与您无关。

答案 2 :(得分:1)

Answer by Andy Turner 是正确的。我只想强调,在现代 Java 中,几乎没有任何理由直接处理 Thread 类。你阅读的教程已经严重过时了;忽略它们。

执行者服务

Java 中加入了 executor 服务框架,抽象出线程管理的繁琐。

查看 Executors 类以实例化 ExecutorService 的各种实现。

ExecutorService es = Executors. … ;

将您的 RunnableCallable 对象提交给执行程序服务。

es.submit( new MyRunnable() ) ;

如果要跟踪任务的完成情况,请捕获 Future 方法返回的 submit 对象。

Project Loom 带来了一项新的执行程序服务,该服务使用轻量级虚拟线程(纤程)来使用更少的内存、执行速度更快、阻塞成本更低,从而允许运行数百万个线程。

答案 3 :(得分:0)

在您的简单示例中,使用共享对象绝对没有缺点(或优点)。但是,如果您的对象中有一个在多个任务中更改的可变变量,您可能会遇到麻烦。

Install-WindowsFeature -name Web-Server -IncludeManagementTools

在这种情况下,您必须使用 class Task { //Getter private long taskId; public void doTask() { taskId++; for (int i = 0; i < 1500; i++) { System.out.print('T'); } } } 关键字或 AtomicLong。否则,您可能会陷入竞争状态,并且 synchronized 会给出错误的值。