我一直在关注一些关于在 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');
}
}
}
现在,我可以启动多个线程并调用我想要同时运行的特定方法。
现在,我想知道使用第一种方法实现多线程操作有什么好处,后一种方法有什么缺点吗?
答案 0 :(得分:3)
使用仅调用 Runnable
的 run()
方法定义 doTask
类是没有意义的。只需将 doTask
的内容放在 run
中。
或者,最好使用 lambda:
Thread thread = new Thread(() -> {
// Stuff you want to do.
});
但绝对没有充分的理由扩展 Thread
。
事实上,在许多情况下,您不想直接使用 Thread
:而是使用 ExecutorService
,您可以向其提交 Runnable
/Callable
实例。
答案 1 :(得分:1)
在java中实现多线程的最佳方式是什么?
没有单一的“最佳”方式。
不同的方式......各有优缺点。
最简单的方法是使用标准的 ExecutorService
实现之一,并将您的任务作为 lambda 表达式或 Runnable
或 Callable
的实例提交。
ForkJoin 池是相似的,但针对不同类型的任务进行了调整,其中一个任务分叉多个子任务然后加入它们;例如分而治之的算法。
另一种方法是使用第 3 方线程池;例如由番石榴提供。
如果你真的需要管理你自己的线程,你可以实现你自己的线程池,但可能有一些东西可以提供你需要的功能。
如果你有一个需要定期运行的任务(在一个线程中),看看TimerTask
或更复杂的任务调度库;例如石英。
如果您遇到线程是静态且长时间运行的情况,您可以直接实例化并启动 Thread
对象,将线程主体作为 Runnable
(或 lambda)传递。< /p>
几乎唯一避免的事情就是编写一个extends
类Thread
的类。这是一个坏主意。如果您遇到一个示例或演示表明这样做,忽略它。扩展 Thread
会将您的业务逻辑嵌入到线程的 run()
方法中。这使得您很难调整您的代码以使用不同的线程管理策略。
推荐阅读:
最后,有一个正在开发轻量级线程模型的 Java 孵化器项目(“Loom”)。这些线被称为“纤维”。它现在可能与您无关。
答案 2 :(得分:1)
Answer by Andy Turner 是正确的。我只想强调,在现代 Java 中,几乎没有任何理由直接处理 Thread
类。你阅读的教程已经严重过时了;忽略它们。
Java 中加入了 executor 服务框架,抽象出线程管理的繁琐。
查看 Executors
类以实例化 ExecutorService
的各种实现。
ExecutorService es = Executors. … ;
将您的 Runnable
或 Callable
对象提交给执行程序服务。
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
会给出错误的值。