每个线程都跟一个Thread实例关联。有两种建立线程的基础策略。
+实例化一个Thread实例,程序异步新建一个线程执行任务(方便直接控制线程的创建和管理)
+传送任务给执行器(executor)执行(从应用的其他部分抽象化线程的管理)
本节使用方法一建立线程,执行器后面章节会介绍到。
1、定义并建立一个线程
定义一个线程需定义线程需提供线程执行的业务逻辑,有两种方式实现:
(1)实现Runable接口
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(); } }
(2)继承Thread类,实现方法run()
public class HelloThread extends Thread { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new HelloThread()).start(); } }
上述的两种风格你应该怎么选择呢。第一种风格是比较常见的,因为实现runnable接口后还可以继承Thread类之外的类。第二种风格在一个简单应用中很容易使用,但受限于它是Thread的子孙类。本节主要使用风格一,它可以将线程(Thread)与任务(Runnable)分离。
风格一不仅比风格而灵活,而且比风格而更适用于高级API(线程池等)。
2、利用睡眠(Sleep)暂停一个线程
Thread.sleep可以暂停当前线程并指定暂停的时间。暂停时,线程的时间片将会让给系统中其他运行的线程或者进程使用。
sleep方法重载了两个版本,一个提供精确到毫秒,另一个则能精确到纳秒。
当然,睡眠时间不能保证精确的,这受限于系统设备。同时线程也可以在睡眠期间因线程中断(interrupts)而终止。
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时候对当前进程进行中断操作触发的。
3、线程中断(interrupts)
中断(interrupt)预示着线程应当停止正在做的事。由开发者来决定线程如何响应中断。
通过调用线程的interrupt方法来对线程发送中断命令。为了让中断机制能正确地工作,被中断线程应该能支持中断。
(1)支持中断
如何做能让一个线程支持中断呢?这取决于这个线程正在整啥犊子。如果它调用一个抛出InterruptedException,通常做法是获取(catch)异常后直接返回(return)。如上面的例子中,如果在runnable中执行,那应修改成下面:
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的又应该怎么处理呢?答案是周期性地调用InterruptedException,如果线程被中断则返回true。如下:
for (int i = 0; i < inputs.length; i++) { heavyCrunch(inputs[i]); if (Thread.interrupted()) { // We've been interrupted: no more crunching. return; } }
上面检测到中断直接退出,而通常在更复杂的程序中会手动抛出一个异常:
if (Thread.interrupted()) { throw new InterruptedException(); }
(2)中断标志(The Interrupt Status Flag)
中断机制(interrupt mechanism)通过线程内部的一个中断标志来判断线程中断状态。调用interrupt方法时候给这个标志设值。当线程通过调用Thread.interrupted来查看线程状态时候 该标志又会被清空。用非静态方法isInterrupted 查询,既不会清空标志,又能查到状态。
当抛出InterruptedException异常时候,也会清空标志。
4、加入(joins)
join函数可让一个线程等待另一个线程运行结束再继续运行。假如t是一个正在跑的线程,
调用
t.join()
将会使当前线程暂停知道t线程终止。join函数的重载函数能指定等待的时间长,当然,这个时间是不能保证精确的。
join、sleep 都会因为线程中断抛出异常。
5、程序示例
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!"); } }