创建和启动Java线程

时间:2020-01-09 10:35:51  来源:igfitidea点击:

Java线程就像一个虚拟CPU,可以在Java应用程序中执行Java代码。当启动Java应用程序时,其main()方法由主线程执行,该特殊线程由Java VM创建以运行应用程序。在应用程序内部,我们可以创建和启动更多线程,这些线程可以与主线程并行执行应用程序代码的某些部分。

Java线程是与任何其他Java对象一样的对象。线程是类java.lang.Thread的实例,或者此类的子类的实例。除了成为对象之外,java线程还可以执行代码。在本Java线程教程中,我将解释如何创建和启动线程。

创建和启动线程

用Java创建线程是这样完成的:

Thread thread = new Thread();

要启动Java线程,我们将调用其start()方法,如下所示:

thread.start();

这个例子没有为线程指定任何代码来执行。因此,线程在启动后将立即再次停止。

有两种方法可以指定线程应执行的代码。首先是创建Thread的子类并覆盖run()方法。第二种方法是将实现Runnable的对象(java.lang.Runnable)传递给Thread构造函数。这两种方法都在下面介绍。

线程子类

指定线程要运行什么代码的第一种方法是创建Thread的子类并覆盖run()方法。 run()方法是调用start()之后线程执行的内容。这是创建JavaThread子类的示例:

public class MyThread extends Thread {

    public void run(){
       System.out.println("MyThread running");
    }
  }

要创建并启动上述线程,我们可以执行以下操作:

MyThread myThread = new MyThread();
  myTread.start();

一旦线程启动,start()调用将返回。它不会等到run()方法完成。 run()方法的执行就像由另一个CPU执行一样。当run()方法执行时,它将打印出文本" MyThread running"。

我们还可以像这样创建Thread的匿名子类:

Thread thread = new Thread(){
    public void run(){
      System.out.println("Thread Running");
    }
  }

  thread.start();

一旦新线程执行run()方法,此示例将打印出文本" Thread running"。

可运行接口的实现

指定线程应运行什么代码的第二种方法是创建一个实现" java.lang.Runnable"接口的类。实现" Runnable"接口的Java对象可以由Java"线程"执行。本教程的稍后部分将介绍如何完成此操作。

" Runnable"接口是Java平台随附的标准Java接口。 " Runnable"接口只有一个方法" run()"。基本上是" Runnable"界面的外观:

public interface Runnable() {

    public void run();

}

在执行时,无论线程应该执行什么操作,都必须包含在run()方法的实现中。有三种方法可以实现" Runnable"接口:

  • 创建一个实现" Runnable"接口的Java类。
  • 创建一个实现" Runnable"接口的匿名类。

创建一个实现" Runnable"接口的Java Lambda。

以下各节将说明所有这三个选项。

Java类实现可运行

实现Java" Runnable"接口的第一种方法是创建自己的Java类,该类实现" Runnable"接口。这是实现" Runnable"接口的自定义Java类的示例:

public class MyRunnable implements Runnable {

    public void run(){
       System.out.println("MyRunnable running");
    }
  }

" Runnable"实现的所有作用是打印出" MyRunnable running"文本。打印完该文本后,将退出run()方法,并且运行run()方法的线程将停止。

Runnable的匿名实现

我们还可以创建" Runnable"的匿名实现。这是实现" Runnable"接口的匿名Java类的示例:

Runnable myRunnable =
    new Runnable(){
        public void run(){
            System.out.println("Runnable running");
        }
    }

除了是一个匿名类之外,该示例与使用自定义类实现" Runnable"接口的示例非常相似。

Java Lambda可运行的实现

实现" Runnable"接口的第三种方法是通过创建" Runnable"接口的Java Lambda实现。这是可能的,因为" Runnable"接口仅具有单个未实现的方法,因此实际上(尽管可能是无意地)是功能性Java接口。

这是实现" Runnable"接口的Java lambda表达式的示例:

Runnable runnable =
        () -> { System.out.println("Lambda Runnable running"); };

使用可运行的线程启动线程

要让线程执行run()方法,请将在其构造函数中将实现Runnable接口的类,匿名类或者lambda表达式的实例传递给其线程中的Thread。这是完成的方式:

Runnable runnable = new MyRunnable(); // or an anonymous class, or lambda...

Thread thread = new Thread(runnable);
thread.start();

当线程启动时,它将调用MyRunnable实例的run()方法,而不是执行它自己的run()方法。上面的示例将打印出文本" MyRunnable running"。

子类还是可运行的?

对于这两种方法中哪一种最好没有任何规则。两种方法都有效。不过,就我个人而言,我更喜欢实现Runnable,并将实现的实例传递给Thread实例。当线程池执行" Runnable"时,很容易将" Runnable"实例排队,直到池中的线程空闲为止。这对于Thread子类来说有点困难。

有时我们可能必须实现Runnable以及子类Thread。例如,如果创建一个可以执行多个Runnable的Thread子类。实现线程池时通常是这种情况。

常见陷阱:调用run()而不是start()

创建和启动线程时,常见的错误是调用Thread的run()方法而不是start(),如下所示:

Thread newThread = new Thread(MyRunnable());
  newThread.run();  //should be start();

起初我们可能不会注意到任何东西,因为Runnable的run()方法按预期执行。但是,它不会由我们刚刚创建的新线程执行。相反,run()方法由创建线程的线程执行。换句话说,该线程执行了以上两行代码。要使新创建的线程newThread调用MyRunnable实例的run()方法,必须调用newThread.start()方法。

线程名称

创建Java线程时,可以为其命名。该名称可以区分不同的线程。例如,如果有多个线程写入" System.out",则可以方便地查看哪个线程写入了文本。这是一个例子:

Thread thread = new Thread("New Thread") {
      public void run(){
        System.out.println("run by: " + getName());
      }
   };

   thread.start();
   System.out.println(thread.getName());

请注意,作为参数传递给" Thread"构造函数的字符串" New Thread"。此字符串是线程的名称。名称可以通过Thread的getName()方法获得。使用Runnable实现时,我们也可以将名称传递给Thread。看起来是这样的:

MyRunnable runnable = new MyRunnable();
   Thread thread = new Thread(runnable, "New Thread");

   thread.start();
   System.out.println(thread.getName());

但是请注意,由于MyRunnable类不是Thread的子类,因此它无权访问执行它的线程的getName()方法。

Thread.currentThread()

Thread.currentThread()方法返回对正在执行currentThread()的Thread实例的引用。这样,我们就可以访问代表执行给定代码块的线程的JavaThread对象。这是一个如何使用Thread.currentThread()的例子:

Thread thread = Thread.currentThread();

一旦有了对Thread对象的引用,就可以在其上调用方法。例如,我们可以获取当前正在执行代码的线程的名称,如下所示:

String threadName = Thread.currentThread().getName();

Java线程示例

这是一个小例子。首先,它打印出执行main()方法的线程的名称。该线程由JVM分配。然后启动10个线程,并给它们一个全名("" + i)。然后,每个线程将其名称打印出来,然后停止执行。

public class ThreadExample {

  public static void main(String[] args){
    System.out.println(Thread.currentThread().getName());
    for(int i=0; i<10; i++){
      new Thread("" + i){
        public void run(){
          System.out.println("Thread: " + getName() + " running");
        }
      }.start();
    }
  }
}

请注意,即使线程按顺序启动(1、2、3等),它们也可能不会顺序执行,这意味着线程1可能不是第一个将其名称写入" System.out"的线程。这是因为线程原则上是并行执行的,而不是顺序执行的。 JVM和/或者操作系统确定线程的执行顺序。此顺序不必与开始时的顺序相同。

暂停线程

线程可以通过调用静态方法Thread.sleep()来暂停自身。 sleep()以毫秒为参数。 sleep()方法将尝试在恢复执行之前休眠该毫秒数。线程sleep()'并非100%精确,但仍然相当不错。这是通过调用Thread``sleep()`方法暂停Java线程3秒钟(3.000毫秒)的示例:

try {
    Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
    e.printStackTrace();
}

执行上述Java代码的线程将休眠大约10秒钟(10,000毫秒)。

停止线程

停止Java线程需要对线程实现代码进行一些准备。 Java的Thread类包含一个stop()方法,但已弃用。原始的stop()方法不能保证线程在什么状态下停止。这意味着,线程在执行期间可以访问的所有Java对象将处于未知状态。如果应用程序中的其他线程也可以访问相同的对象,则应用程序可能会意外失败并且无法预期。

不必调用stop()方法,我们必须实现线程代码,以便可以将其停止。这是一个实现Runnable的类的示例,其中包含一个名为doStop()的额外方法,该方法向Runnable发出停止信号。 " Runnable"将检查该信号,并在准备就绪时将其停止。

public class MyRunnable implements Runnable {

    private boolean doStop = false;

    public synchronized void doStop() {
        this.doStop = true;
    }

    private synchronized boolean keepRunning() {
        return this.doStop == false;
    }

    @Override
    public void run() {
        while(keepRunning()) {
            // keep doing what this thread should do.
            System.out.println("Running");

            try {
                Thread.sleep(3L * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

注意doStop()keepRunning()方法。 " doStop()"旨在从另一个线程中执行,而不是执行" MyRunnable"的" run()"方法的线程。执行内部MyRunnable的run()方法的线程在内部调用keepRunning()方法。只要没有调用doStop(),keepRunning()方法将返回true,这意味着执行run()方法的线程将继续运行。

这是一个启动Java线程的示例,该线程执行上述MyRunnable类的实例,并在延迟后再次将其停止:

public class MyRunnableMain {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        Thread thread = new Thread(myRunnable);

        thread.start();

        try {
            Thread.sleep(10L * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        myRunnable.doStop();
    }
}

本示例首先创建一个" MyRunnable"实例,然后将该实例传递给线程并启动该线程。然后执行main()方法的线程(主线程)休眠10秒钟,然后调用MyRunnable实例的doStop()方法。这将导致执行" MyRunnable"方法的线程停止,因为在调用" doStop()"之后," keepRunning()"将返回" true"。

请记住,如果Runnable实现不仅仅需要run()方法(例如,stop()或者pause()方法),那么我们将无法再创建Runnable。 Java lambda表达式实现。 Java lambda只能实现一个方法。取而代之的是,我们必须使用自定义类,或者使用扩展了具有额外方法且由匿名类实现的" Runnable"的自定义接口。