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 Life Cycle in Java
Java 线程生命周期

如上图所示,线程生命周期有各种阶段

  1. 新建
  2. 可运行
  3. 运行中
  4. 等待
  5. 死亡
  1. 新建:在此阶段,使用“Thread class”创建线程。在程序启动线程之前,它一直处于此状态。它也称为生线程。
  2. 可运行:在此页面,调用线程实例的 start 方法。线程控制权交给调度程序以完成执行。是否运行线程取决于调度程序。
  3. 运行中:当线程开始执行时,状态会变为“运行中”状态。调度程序从线程池中选择一个线程,并在应用程序中开始执行。
  4. 等待:这是线程必须等待的状态。由于应用程序中运行着多个线程,因此需要线程之间的同步。因此,一个线程必须等待,直到另一个线程执行完成。因此,此状态称为等待状态。
  5. 死亡:这是线程终止的状态。线程处于运行状态,一旦完成处理,就处于“死亡状态”。


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:我们在这里编写文本,说明线程正在运行。

当您执行上述代码时,您会得到以下输出

Thread example in Java

输出

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 的名称。

当您执行上述代码时,您会得到以下输出

Java Multithreading Example

输出

线程名称在此输出为

  • 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() 方法启动线程。

当您执行上述代码时,您会得到以下输出

Multithreading Example in Java

输出:

有两个线程,因此我们得到两次消息“线程已启动”。

我们获得了线程的名称,因为我们已经输出了它们。

它进入 for 循环,我们在其中打印计数器和线程名称,计数器从 0 开始。

循环执行三次,在此期间线程休眠 1000 毫秒。

因此,首先,我们得到 guru1,然后是 guru2,然后又回到 guru2,因为线程在这里休眠 1000 毫秒,然后下一个 guru1,又回到 guru1,线程休眠 1000 毫秒,所以我们得到 guru2,然后是 guru1。

摘要

在本教程中,我们了解了 Java 中的多线程应用程序以及如何在 Java 中使用单线程和多线程。

  • 解释 Java 中的多线程:在多线程中,用户不会被阻塞,因为线程是独立的,并且可以同时执行多个操作
  • 线程的各个生命周期阶段是:
    • 新建
    • 可运行
    • 运行中
    • 等待
    • 死亡
  • 我们还了解了线程之间的同步,这有助于应用程序平稳运行。
  • Java 中的多线程编程使许多应用程序任务更加容易。