用Java编写多线程代码的4种方法

多线程是一种编写用于并行执行任务的代码的方法。从Java 1.0的早期开始,Java就对编写多线程代码提供了出色的支持。 Java的最新增强功能增加了将代码构造为在Java程序中合并多线程的方式。

在本文中,我们比较了其中一些选项,以便您可以更好地判断使用哪个选项

方法1:扩展Thread类

Java提供了一个 Thread 类,可以扩展该类以实现 run()方法。这个run()方法是您执行任务的地方。当您要在其自己的线程中启动任务时,可以创建此类的实例并调用其 start()方法。这将开始线程执行并运行到完成(或终止于异常)。

这里是一个简单的Thread类,它只是在指定的时间间隔内睡眠,作为模拟长时间运行的操作的方式。

public class MyThread extends Thread{  private int sleepFor;  public MyThread(int sleepFor) {    this.sleepFor = sleepFor;  }  @Override  public void run() {    System.out.printf("[%s] thread starting\n",    Thread.currentThread().toString());    try { Thread.sleep(this.sleepFor); }    catch(InterruptedException ex) {}    System.out.printf("[%s] thread ending\n",    Thread.currentThread().toString());  }}

通过为其提供睡眠时间(毫秒)来创建此Thread类的实例。

MyThread worker = new MyThread(sleepFor);

通过调用其工作线程的start()方法开始执行该工作线程。此方法立即将控制权返回给调用方,而无需等待线程终止。

worker.start();System.out.printf("[%s] main thread\n", Thread.currentThread().toString());

这是运行此代码的输出。它指示在工作线程执行之前已打印主线程诊断。

[Thread[main,5,main]] main thread[Thread[Thread-0,5,main]] thread starting[Thread[Thread-0,5,main]] thread ending

由于在启动工作线程之后不再有其他语句,因此主线程在程序退出之前等待工作线程完成。方法2:将线程实例与可运行对象一起使用

Java还提供了名为 Runnable 的接口,该接口可以运行任务。由worker类实现,以执行其 run()方法中的任务。与扩展 Thread 类(如上所述)相反,这是创建工作类的另一种方法。

这里是worker类的实现,该类现在实现了Runnable。

public class MyThread2 implements Runnable {  // same as above}

实现Runnable接口而不是扩展Thread类的优点是,工人类现在可以在类层次结构中扩展特定于域的类。

这是什么意思?

让我们说,例如,您有一个实现水果某些通用特性的 Fruit 类。现在,您要实现一个木瓜类,该类专门研究某些水果特性。您可以通过使 Papaya 类扩展 Fruit 类来实现。

public class Fruit {  // fruit specifics here}public class Papaya extends Fruit {  // override behavior specific to papaya here}

现在,假设您有一些需要Papaya支持的耗时任务,可以在单独的线程中执行。这种情况可以通过使Papaya类实现Runnable并在执行此任务的地方提供run()方法来解决。

public class Papaya extends Fruit implements Runnable {  // override behavior specific to papaya here  @Override  public void run() {    // time consuming task here.  }}

要启动工作线程,您可以创建worker类的实例并手动执行在创建时将其转移到Thread实例。调用Thread的start()方法时,任务将在单独的线程中执行。

Papaya papaya = new Papaya();// set properties and invoke papaya methods here.Thread thread = new Thread(papaya);thread.start();

这是如何使用Runnable来实现在线程中执行的任务的简短摘要。

方法3:使用ExecutorService执行可运行程序

从版本1.5开始,Java提供了 ExecutorService 作为用于在程序中创建和管理线程的新范例。 。它通过抽象化线程的创建来概括线程执行的概念。

这是因为您可以像在每个任务中使用单独的线程一样容易地在线程池中运行任务。这样,您的程序就可以跟踪和管理用于工作程序任务的线程数。

Suppose you have a 100 worker tasks waiting to be executed. If you start one thread per worker (as presented above), you would have 100 threads within your program which might lead to bottlenecks elsewhere within the program. Instead, if you use a thread pool with, say 10 threads pre-allocated, your 100 tasks will be executed by these threads one after another so your program is not starved for resources. In addition, these thread pool threads can be configured so that they hang around to perform additional tasks for you.

ExecutorService接受 Runnable 任务(如上所述),并在合适的位置运行任务时间。接受Runnable任务的 submit()方法返回一个名为 Future 的类的实例,该类使调用者可以跟踪任务的状态。特别地, get()方法允许调用方等待任务完成(并提供返回码(如果有))。

在下面的示例中,我们创建使用静态方法 newSingleThreadExecutor()的ExecutorService可以创建一个用于执行任务的线程。如果在执行一项任务时提交了更多任务,则ExecutorService将这些任务排队等待后续执行。

我们在此使用的Runnable实现与上述相同。

ExecutorService esvc = Executors.newSingleThreadExecutor();Runnable worker = new MyThread2(sleepFor);Future<?> future = esvc.submit(worker);System.out.printf("[%s] main thread\n", Thread.currentThread().toString());future.get();esvc.shutdown();

注意

方法4:与ExecutorService一起使用的可调用对象

从版本1.5开始,Java引入了新的方法。称为 Callable 的界面。它与较早的Runnable接口相似,不同之处在于执行方法(称为 call()而不是 run())可以返回一个值。此外,它还可以声明可以抛出 Exception

ExecutorService还可以接受以 Callable 实现的任务,并返回将来,并带有方法完成时返回的值。

这里是一个示例 Mango 类,它扩展了先前定义的 Fruit 类,实现 Callable 接口。在 call()方法中执行了一项昂贵且耗时的任务。

public class Mango extends Fruit implements Callable {  public Integer call() {    // expensive computation here    return new Integer(0);  }}

这是用于将类的实例提交给ExecutorService的代码。下面的代码还等待任务完成并打印其返回值。

ExecutorService esvc = Executors.newSingleThreadExecutor();MyCallable worker = new MyCallable(sleepFor);Future future = esvc.submit(worker);System.out.printf("[%s] main thread\n", Thread.currentThread().toString());System.out.println("Task returned: " + future.get());esvc.shutdown();

您更喜欢什么?

在本文中,我们学习了几种编写多线程方法Java代码。其中包括:

  • 扩展 Thread 类是最基本的类,并且从Java 1.0开始可用。
  • 如果您有一个必须扩展其他类的类,类,然后可以实现 Runnable 接口。
  • 创建线程的更现代的工具是 ExecutorService ,它可以接受Runnable实例作为要运行的任务。此方法的优点是可以将线程池​​用于任务执行。线程池通过重用线程来帮助节省资源。
  • 最后,您还可以通过实现 Callable 接口并将任务提交给ExecutorService来创建任务。
  • 标签: