如何在 Java 中开始使用多线程
什么是线程
什么是多线程?
什么是并发性
为什么需要多线程?
如何在Java中编写多线程程序
代码
恭喜😃
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
本文最初发表于adityasridhar.com。
什么是线程
线程是一种轻量级进程。任何进程都可以运行多个线程。
例如,在网页浏览器中,我们可以有一个线程来加载用户界面,还可以有另一个线程来实际检索需要在用户界面中显示的所有数据。
什么是多线程?
多线程技术使我们能够同时运行多个线程。
例如,在网页浏览器中,我们可以有一个线程来处理用户界面,同时还可以有一个线程来获取要显示的数据。
因此,多线程可以提高系统的响应速度。
什么是并发性
线程上下文中的并发性使我们能够同时运行多个线程。
但这些线程真的同时运行吗?
单核系统
JVM提供的线程调度器决定在任何给定时间运行哪个线程。调度器会为每个线程分配一个很小的时间片。
因此,在任何给定时刻,处理器中实际运行的线程只有一个。但由于时间片轮转机制,我们会感觉好像有多个线程同时运行。
多核系统
即使在多核系统中,线程调度器也参与其中。但由于我们有多个核心,因此实际上可以同时运行多个线程。
例如,如果我们有一个双核系统,那么我们可以同时运行两个线程。第一个线程在第一个核心上运行,第二个线程在第二个核心上运行。
为什么需要多线程?
多线程技术能够提高系统的响应速度。
例如,在网页浏览器中,如果所有操作都在单个线程中运行,那么在获取数据进行显示时,系统将完全无响应。如果获取数据需要 10 秒钟,那么在这 10 秒钟内,我们将无法在网页浏览器中执行任何其他操作,例如打开新标签页,甚至关闭浏览器。
因此,在不同的线程中同时运行程序的不同部分有助于提高系统的响应速度。
如何在Java中编写多线程程序
我们可以使用以下方法在 Java 中创建线程
- 扩展线程类
- 实现 Runnable 接口
- 实现 Callable 接口
- 通过使用执行器框架以及可运行任务和可调用任务
我们将在另一篇博客文章中详细介绍 Callable 和 Executor 框架。本文主要关注扩展 Thread 类和实现 Runnable 接口。
扩展线程类
为了创建一段可以在线程中运行的代码,我们需要创建一个类,然后继承Thread类。这段代码要执行的任务需要放在`run()`函数中。在下面的代码中,你可以看到`Worker`类继承自Thread类,而打印 0 到 5 的数字的任务就在`run()`函数中执行。
class Worker extends Thread {
@Override
public void run() {
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
上面的代码中使用了Thread.currentThread().getName()来获取正在运行代码的当前线程的名称。
要创建一个线程,我们只需要创建一个 Worker 类的实例。然后我们就可以使用start()函数启动线程了。
public class ThreadClassDemo {
public static void main(String[] args) {
Thread t1 = new Worker();
Thread t2 = new Worker();
Thread t3 = new Worker();
t1.start();
t2.start();
t3.start();
}
}
在上面的代码中,我们使用 Worker 类创建了 3 个线程 t1、t2 和 t3。然后我们使用start()
函数 启动这些线程。
以下是通过扩展 Thread 类来创建线程的最终代码
class Worker extends Thread {
@Override
public void run() {
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class ThreadClassDemo {
public static void main(String[] args) {
Thread t1 = new Worker();
Thread t2 = new Worker();
Thread t3 = new Worker();
t1.start();
t2.start();
t3.start();
}
}
以下是运行上述代码后得到的输出结果。
可以看出,这 3 个线程都打印出了 0 到 5 之间的数字。
从输出结果可以清楚地看出,这三个线程并没有按照任何特定的顺序运行。
实现 Runnable 接口
为了创建一段可以在线程中运行的代码,我们需要创建一个类并实现Runnable接口。这段代码要执行的任务需要放在run()函数中。在下面的代码中,你可以看到RunnableWorker是一个实现了Runnable接口的类,而打印 0 到 4 的数字的任务就在run()函数中执行。
class RunnableWorker implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 4; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
要创建一个线程,首先需要创建一个实现Runnable接口的RunnableWorker实例。
然后,我们可以通过创建一个Thread类的实例并将RunnableWorker实例作为参数传递来创建一个新线程。以下代码展示了这一点。
public class RunnableInterfaceDemo {
public static void main(String[] args) {
Runnable r = new RunnableWorker();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
上述代码创建了一个 Runnable 实例 r。然后,它创建了三个线程 t1、t2 和 t3,并将r作为参数传递给这三个线程。最后,使用start()函数启动所有三个线程。
以下是实现 Runnable 接口创建线程的完整代码。
class RunnableWorker implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 4; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class RunnableInterfaceDemo {
public static void main(String[] args) {
Runnable r = new RunnableWorker();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
运行上述代码后,我们将得到以下输出。每次运行代码,输出顺序都会改变。
实现 Runnable 接口比扩展 Thread 类更好,因为我们只能扩展一个类,但在 Java 中我们可以实现多个接口。
Java 8 中的可运行接口
在 Java 8 中,Runnable 接口变成了FunctionalInterface,因为它只有一个函数run()。
下面的代码展示了如何在 Java 8 中创建 Runnable 实例。
public class RunnableFunctionalInterfaceDemo {
public static void main(String[] args) {
Runnable r = () -> {
for (int i = 0; i <= 4; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
这里,我们无需创建类并实现 Runnable 接口,可以直接使用 lambda 表达式创建 Runnable 实例,如下所示。
Runnable r = () -> {
for (int i = 0; i <= 4; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
};
代码
本文中的代码可在以下 GitHub 仓库中找到
:https://github.com/aditya-sridhar/basic-threads-demo
恭喜😃
现在您知道如何通过扩展 Thread 类并实现 Runnable 接口来创建线程了。
我将在下一篇博客文章中讨论线程生命周期以及使用线程时遇到的挑战。

