Java线程由Thread类表示。下面开始讨论Thread类!
定义和启动线程
实例化一个Thread对象,有两种实现方法:
- 实现Runnable接口。Runnable接口仅定义了方法run,用于包含在单独线程中运行的代码。将Runnable对象传递给Thread构造函数,如下所示:
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方法,如下所示:
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接口更为通用,因为Java不允许多重继承,如果继承了Thread类,就不能继承其他类,但是实现接口没关系。继承Thread类则更为简单,使用Thread包含一些方法很方便,在一些简单的应用中很好用。
两种方法都通过Thread.start启动线程。Thread类还包含许多用于线程管理的有用犯非法,包括一些用于改变及获得线程状态的静态方法。
暂停线程
Thread.sleep暂停当前线程。主要作用是让出处理器时间给其他线程或应用。
有两个重载的sleep方法:一个指定微秒的睡眠时间,一个指定纳秒的睡眠时间。需要注意的是,指定的睡眠时间不能保证完全准确,睡眠多长时间和底层OS相关。另外,睡眠过程可被打断。在任何情况下,都不要用sleep的确切时间进行程序设计。
如SleepMessages所示,它会每4秒打印一条信息:
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抛出的异常可以看出,当sleep被其他线程打断时,会抛出InterruptedException异常。
线程中断
中断,即让线程停止运行。线程究竟如何中断由自己实现,一般直接结束线程。
Thread通过调用interrupt中断线程。那么一个线程如何中断?
中断支持
一个线程如何中断,取决于线程正在干什么。如果一个线程经常调用会抛出InterruptedExceptiond的方法,则在抛出异常后,会直接从run方法返回,从而中断。我们对上面的SleepMessages稍微修改一下以支持中断:
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;
}
}
在上例中,通过判断线程是否被中断,来觉得是否应该返回。对于比较复杂的程序,直接抛出异常比较好:
if (Thread.interrupted()) {
throw new InterruptedException();
}
从而能把精力集中在catch子句的编写。
中断状态flag
中断机制是通过内部的一个中断状态标签来实现的。调用Thread.interrupt时会设置该flag。当通过Thread.interrupted检测中断状态时,该flag被清除。而通过非静态方法isInterrupted检查,则不会清除该flag。
Joins
join方法使一个线程在另外一个线程结束之后再执行。假如t是当前要运行的Thread
t.join();
调用join之后,当前线程暂停运行直到线程t结束。join有个重载方法,可以定义等待的时间,不过和sleep方法一样,这个时间也不是完全准确的,在被打断时也抛出InterruptedException。
实例:
SimpleThreads包含两个线程,首先是main线程,然后在main里新建了一个MessageLoop线程,并且等待该线程运行完成,如果MessageLoop运行线程过长,main就打断它。
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!");
}
}