每一个线程都与Thread类的实例相关联,使用Thread
对象建立并发应用程序有两种基本策略。html
Thread
。本节介绍Thread
对象的使用,Executors
将与其余高级并发对象一块儿讨论。java
建立Thread
实例的应用程序必须提供将在该线程中运行的代码,有两种方法能够作到这一点:git
Runnable
对象,Runnable接口定义了一个单独的run
方法,用于包含在线程中执行的代码,Runnable
对象被传递给Thread
构造函数,如HelloRunnable示例中所示:public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
Thread
,Thread
类自己实现了Runnable
,尽管它的run
方法什么都不作,应用程序能够子类化Thread
,提供本身的run
实现,如HelloThread示例中所示:public class HelloThread extends Thread { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new HelloThread()).start(); } }
请注意,两个示例都调用Thread.start
以启动新线程。程序员
你应该使用哪一个语法?使用Runnable
对象的第一个语法更通用,由于Runnable
对象能够继承Thread
之外的类。第二个语法在简单的应用程序中更容易使用,但受限于你的任务类必须是Thread
的后代这一事实。本课重点介绍第一种方法,该方法将Runnable
任务与执行任务的Thread
对象分开,这种方法不只更灵活,并且适用于后面介绍的高级线程管理API。github
Thread
类定义了许多对线程管理有用的方法,这些包括静态方法,它们提供关于调用该方法的线程的信息,或影响该线程的状态。其余方法是从管理线程和Thread
对象的其余线程调用的,咱们将在如下部分中研究其中一些方法。segmentfault
Thread.sleep
致使当前线程暂停执行指定的时间段,这是使处理器时间可用于应用程序的其余线程或可能在计算机系统上运行的其余应用程序的有效方法。sleep
方法也能够用于调步,以下面的示例所示,和等待具备被理解为具备时间要求的职责的另外一个线程,如稍后部分中的SimpleThreads
示例。api
提供了两个重载版本的sleep
:一个指定毫秒的睡眠时间,一个指定纳秒的睡眠时间。可是,这些睡眠时间并不能保证精确,由于它们受到底层操做系统提供的设施的限制,此外,睡眠周期能够经过中断终止,咱们将在后面的部分中看到。在任何状况下,你都不能设想调用sleep
会准确地在指定的时间段内暂停该线程。并发
SleepMessages示例使用sleep
以四秒为间隔打印消息:oracle
public class SleepMessages { public static void main(String args[]) throws InterruptedException { String importantInfo[] = { "Mares eat oats", "Does eat oats", "Little lambs eat ivy", "A kid will eat ivy too" }; for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds Thread.sleep(4000); //Print a message System.out.println(importantInfo[i]); } } }
请注意,main
声明抛出InterruptedException
,这是一个异常,当sleep
处于活动状态时,另外一个线程中断当前线程时,sleep
将抛出,因为此应用程序还没有定义另外一个致使中断的线程,所以无需捕获InterruptedException
。异步
中断是指示线程应该中止正在作的事情,并执行其余操做,由程序员决定线程如何响应中断,但用于终止线程是很常见的,这是本课程中强调的用法。
线程经过调用Thread
对象上的interrupt来发送中断,以便线程被中断,为使中断机制正常工做,被中断的线程必须支持本身的中断。
线程如何支持本身的中断?这取决于它目前正在作什么,若是线程常常调用抛出InterruptedException
的方法,它只会在捕获该异常后从run
方法返回。例如,假设SleepMessages
示例中的中心消息循环位于线程的Runnable
对象的run
方法中,而后能够按以下方式修改它以支持中断:
for (int i = 0; i < importantInfo.length; i++) { // Pause for 4 seconds try { Thread.sleep(4000); } catch (InterruptedException e) { // We've been interrupted: no more messages. return; } // Print a message System.out.println(importantInfo[i]); }
许多抛出InterruptedException
的方法(例如sleep
)被设计为收到中断时取消当前操做并当即返回。
若是一个线程长时间运行而不调用抛出InterruptedException
的方法呢?那么它必须按期调用Thread.interrupted
,若是收到中断,则返回true
,例如:
for (int i = 0; i < inputs.length; i++) { heavyCrunch(inputs[i]); if (Thread.interrupted()) { // We've been interrupted: no more crunching. return; } }
在这个简单的例子中,代码只是测试中断,若是收到中断则退出线程,在更复杂的应用程序中,抛出InterruptedException
可能更有意义:
if (Thread.interrupted()) { throw new InterruptedException(); }
这容许中断处理代码集中在catch
子句中。
中断机制使用称为中断状态的内部标志来实现,调用Thread.interrupt
设置此标志,当线程经过调用静态方法Thread.interrupted
来检查中断时,将清除中断状态,非静态isInterrupted
方法,由一个线程用于查询另外一个线程的中断状态,不会更改中断状态标志。
按照惯例,任何经过抛出InterruptedException
退出的方法都会在执行此操做时清除中断状态,可是,经过另外一个线程调用中断,老是能够当即再次设置中断状态。
join
方法容许一个线程等待另外一个线程的完成,若是t
是其线程当前正在执行的Thread
对象:
t.join();
致使当前线程暂停执行,直到t
的线程终止,join
重载方法容许程序员指定等待周期,可是,与sleep
同样,join
依赖于OS进行计时,所以你不该该设想join
将准确地等待你指定的时间。
与sleep
同样,join
经过InterruptedException
退出来响应中断。
如下示例汇总了本节的一些概念,SimpleThreads由两个线程组成。第一个是每一个Java应用程序都有的主线程,主线程从Runnable
对象MessageLoop
建立一个新线程,并等待它完成,若是MessageLoop
线程须要很长时间才能完成,主线程会中断它。
MessageLoop
线程打印出一系列消息,若是在打印完全部消息以前被中断,MessageLoop
线程将打印一条消息并退出。
public class SimpleThreads { // Display a message, preceded by // the name of the current thread static void threadMessage(String message) { String threadName = Thread.currentThread().getName(); System.out.format("%s: %s%n", threadName, message); } private static class MessageLoop implements Runnable { public void run() { String importantInfo[] = { "Mares eat oats", "Does eat oats", "Little lambs eat ivy", "A kid will eat ivy too" }; try { for (int i = 0; i < importantInfo.length; i++) { // Pause for 4 seconds Thread.sleep(4000); // Print a message threadMessage(importantInfo[i]); } } catch (InterruptedException e) { threadMessage("I wasn't done!"); } } } public static void main(String args[]) throws InterruptedException { // Delay, in milliseconds before // we interrupt MessageLoop // thread (default one hour). long patience = 1000 * 60 * 60; // If command line argument // present, gives patience // in seconds. if (args.length > 0) { try { patience = Long.parseLong(args[0]) * 1000; } catch (NumberFormatException e) { System.err.println("Argument must be an integer."); System.exit(1); } } threadMessage("Starting MessageLoop thread"); long startTime = System.currentTimeMillis(); Thread t = new Thread(new MessageLoop()); t.start(); threadMessage("Waiting for MessageLoop thread to finish"); // loop until MessageLoop // thread exits while (t.isAlive()) { threadMessage("Still waiting..."); // Wait maximum of 1 second // for MessageLoop thread // to finish. t.join(1000); if (((System.currentTimeMillis() - startTime) > patience) && t.isAlive()) { threadMessage("Tired of waiting!"); t.interrupt(); // Shouldn't be long now // -- wait indefinitely t.join(); } } threadMessage("Finally!"); } }