创建和启动Java线程
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"的自定义接口。