Java 中的多线程
任何应用程序都可以有多个进程(实例)。每个进程都可以被分配为单个线程或多个线程。在本教程中,我们将了解如何同时执行多个任务,并进一步了解线程以及线程之间的同步。
什么是单线程?
Java 中的单线程基本上是轻量级的,也是最小的处理单元。Java 使用“线程类”来使用线程。线程有两种类型——用户线程和守护线程(守护线程用于清理应用程序并在后台使用)。当应用程序首次启动时,会创建用户线程。之后,我们可以创建许多用户线程和守护线程。
单线程示例
package demotest; public class GuruThread { public static void main(String[] args) { System.out.println("Single Thread"); } }
单线程的优点
- 由于单线程在系统中执行,因此减少了应用程序的开销
- 此外,它降低了应用程序的维护成本。
Java 中的多线程是什么?
Java 中的多线程是通过最大化 CPU 利用率来同时执行两个或多个线程的过程。多线程应用程序执行两个或多个线程并发运行。因此,它也称为 Java 中的并发。每个线程彼此并行运行。多线程不分配单独的内存区域,因此它们节省内存。此外,线程之间的上下文切换耗时更少。
多线程示例
package demotest; public class GuruThread1 implements Runnable { public static void main(String[] args) { Thread guruThread1 = new Thread("Guru1"); Thread guruThread2 = new Thread("Guru2"); guruThread1.start(); guruThread2.start(); System.out.println("Thread names are following:"); System.out.println(guruThread1.getName()); System.out.println(guruThread2.getName()); } @Override public void run() { } }
多线程的优点
- 用户不会被阻塞,因为线程是独立的,我们可以同时执行多个操作
- 由于线程是独立的,如果一个线程遇到异常,其他线程不会受到影响。
Java 线程生命周期
线程的生命周期
如上图所示,线程生命周期有各种阶段
- 新建
- 可运行
- 运行中
- 等待
- 死亡
- 新建:在此阶段,使用“Thread class”创建线程。在程序启动线程之前,它一直处于此状态。它也称为生线程。
- 可运行:在此页面,调用线程实例的 start 方法。线程控制权交给调度程序以完成执行。是否运行线程取决于调度程序。
- 运行中:当线程开始执行时,状态会变为“运行中”状态。调度程序从线程池中选择一个线程,并在应用程序中开始执行。
- 等待:这是线程必须等待的状态。由于应用程序中运行着多个线程,因此需要线程之间的同步。因此,一个线程必须等待,直到另一个线程执行完成。因此,此状态称为等待状态。
- 死亡:这是线程终止的状态。线程处于运行状态,一旦完成处理,就处于“死亡状态”。
Java 多线程方法
线程的一些常用方法包括方法 | 描述 |
---|---|
start() | 此方法启动线程的执行,并且 JVM 调用线程的 run() 方法。 |
Sleep(int milliseconds) | 此方法使线程休眠,因此线程的执行将暂停提供的毫秒数,之后线程将再次开始执行。这有助于线程的同步。 |
getName() | 它返回线程的名称。 |
setPriority(int newpriority) | 它更改线程的优先级。 |
yield () | 它会导致当前线程暂停,而其他线程执行。 |
示例:在这个 Java 多线程程序示例中,我们将创建一个线程并探索可用于线程的内置方法。
package demotest; public class thread_example1 implements Runnable { @Override public void run() { } public static void main(String[] args) { Thread guruthread1 = new Thread(); guruthread1.start(); try { guruthread1.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } guruthread1.setPriority(1); int gurupriority = guruthread1.getPriority(); System.out.println(gurupriority); System.out.println("Thread Running"); } }
代码解释
- 代码行 2:我们正在创建一个实现 Runnable 接口的类“thread_Example1”(任何实例打算由线程执行的类都应实现该接口)。
- 代码行 4:它覆盖了 runnable 接口的 run 方法,因为必须覆盖该方法。
- 代码行 6:在这里,我们在其中启动线程执行的 main 方法。
- 代码行 7:我们正在通过实例化新线程类来创建一个名为“guruthread1”的新线程。
- 代码行 8:我们将使用“guruthread1”实例的线程“start”方法。在这里,线程将开始执行。
- 代码行 10:我们正在使用“guruthread1”实例的线程“sleep”方法。因此,线程将休眠 1000 毫秒。
- 代码行 9-14:我们已将 sleep 方法放入 try catch 块中,因为会出现已检查的异常,即 Interrupted exception。
- 代码行 15:无论线程的优先级是什么,我们都将其优先级设置为 1。
- 代码行 16:我们正在使用 getPriority() 获取线程的优先级。
- 代码行 17:我们正在打印从 getPriority 获取的值。
- 代码行 18:我们在这里编写文本,说明线程正在运行。
当您执行上述代码时,您会得到以下输出
输出
5 是线程优先级,Thread Running 是代码的输出文本。
Java 线程同步
在多线程中,程序存在异步行为。如果一个线程正在写入某些数据,而另一个线程同时读取数据,则可能导致应用程序不一致。当需要两个或多个线程访问共享资源时,将使用同步方法。Java 提供了 synchronized 方法来实现同步行为。
在这种方法中,一旦线程进入 synchronized 块,其他任何线程都无法在该对象上调用该方法。所有线程都必须等待,直到该线程完成 synchronized 块并退出。通过这种方式,同步有助于多线程应用程序。一个线程必须等待,直到另一个线程完成其执行,然后其他线程才允许执行。
它可以写成以下形式
Synchronized(object) { //Block of statements to be synchronized }
Java 中的多线程示例程序
在这个 Java 多线程示例中,我们将使用两个线程并获取线程的名称。
示例 1
GuruThread1.java package demotest; public class GuruThread1 implements Runnable{ /** * @param args */ public static void main(String[] args) { Thread guruThread1 = new Thread("Guru1"); Thread guruThread2 = new Thread("Guru2"); guruThread1.start(); guruThread2.start(); System.out.println("Thread names are following:"); System.out.println(guruThread1.getName()); System.out.println(guruThread2.getName()); } @Override public void run() { } }
代码解释
- 代码行 3:我们使用了一个实现 Runnable 的类“GuruThread1”(任何实例打算由线程执行的类都应实现该接口)。
- 代码行 8:这是该类的 main 方法。
- 代码行 9:我们在这里实例化 Thread 类并创建一个名为“guruThread1”的实例并创建一个线程。
- 代码行 10:我们在这里实例化 Thread 类并创建一个名为“guruThread2”的实例并创建一个线程。
- 代码行 11:我们正在启动线程,即 guruThread1。
- 代码行 12:我们正在启动线程,即 guruThread2。
- 代码行 13:输出文本“线程名称如下:”。
- 代码行 14:使用线程类的 getName() 方法获取线程 1 的名称。
- 代码行 15:使用线程类的 getName() 方法获取线程 2 的名称。
当您执行上述代码时,您会得到以下输出
输出
线程名称在此输出为
- Guru1
- Guru2
示例 2
在这个 Java 多线程示例中,我们将学习如何覆盖 runnable 接口的 run() 和 start() 方法,并创建该类的两个线程并相应地运行它们。
此外,我们还有两个类,
- 一个实现 runnable 接口,
- 另一个具有 main 方法并相应执行。
package demotest; public class GuruThread2 { public static void main(String[] args) { // TODO Auto-generated method stub GuruThread3 threadguru1 = new GuruThread3("guru1"); threadguru1.start(); GuruThread3 threadguru2 = new GuruThread3("guru2"); threadguru2.start(); } } class GuruThread3 implements Runnable { Thread guruthread; private String guruname; GuruThread3(String name) { guruname = name; } @Override public void run() { System.out.println("Thread running" + guruname); for (int i = 0; i < 4; i++) { System.out.println(i); System.out.println(guruname); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Thread has been interrupted"); } } } public void start() { System.out.println("Thread started"); if (guruthread == null) { guruthread = new Thread(this, guruname); guruthread.start(); } } }
代码解释
- 代码行 2:我们在这里使用一个类“GuruThread2”,它将包含 main 方法。
- 代码行 4:我们在这里使用该类的 main 方法。
- 代码行 6-7:我们在这里创建了 GuruThread3 类(将在代码下方创建)的一个实例,名为“threadguru1”,并启动了线程。
- 代码行 8-9:我们在这里创建了 GuruThread3 类(将在代码下方创建)的另一个实例,名为“threadguru2”,并启动了线程。
- 代码行 11:我们在这里创建了一个实现 runnable 接口的类“GuruThread3”(任何实例打算由线程执行的类都应实现该接口)。
- 代码行 13-14:我们使用了两个类变量,一个类型为线程类,另一个类型为字符串类。
- 代码行 15-18:我们覆盖了 GuruThread3 构造函数,它接受一个字符串类型的参数(即线程名称),该参数被赋给类变量 guruname,从而存储了线程的名称。
- 代码行 20:我们在这里覆盖了 runnable 接口的 run() 方法。
- 代码行 21:我们使用 println 语句输出线程名称。
- 代码行 22-31:我们在这里使用一个 for 循环,其计数器初始化为 0,并且不应小于 4(我们可以取任何数字,所以这里循环会运行 4 次),并增加计数器。我们正在打印线程名称,并在 try-catch 块中将线程休眠 1000 毫秒,因为 sleep 方法会引发已检查的异常。
- 代码行 33:我们在这里覆盖了 runnable 接口的 start 方法。
- 代码行 35:我们输出文本“线程已启动”。
- 代码行 36-40:我们在这里使用一个 if 条件来检查类变量 guruthread 是否有值。如果它是 null,那么我们就使用线程类创建一个实例,该类以参数(在构造函数中赋值)作为参数。之后,通过 start() 方法启动线程。
当您执行上述代码时,您会得到以下输出
输出:
有两个线程,因此我们得到两次消息“线程已启动”。
我们获得了线程的名称,因为我们已经输出了它们。
它进入 for 循环,我们在其中打印计数器和线程名称,计数器从 0 开始。
循环执行三次,在此期间线程休眠 1000 毫秒。
因此,首先,我们得到 guru1,然后是 guru2,然后又回到 guru2,因为线程在这里休眠 1000 毫秒,然后下一个 guru1,又回到 guru1,线程休眠 1000 毫秒,所以我们得到 guru2,然后是 guru1。
摘要
在本教程中,我们了解了 Java 中的多线程应用程序以及如何在 Java 中使用单线程和多线程。
- 解释 Java 中的多线程:在多线程中,用户不会被阻塞,因为线程是独立的,并且可以同时执行多个操作
- 线程的各个生命周期阶段是:
- 新建
- 可运行
- 运行中
- 等待
- 死亡
- 我们还了解了线程之间的同步,这有助于应用程序平稳运行。
- Java 中的多线程编程使许多应用程序任务更加容易。