Chapter 1.2 线程对象
本文翻译自Oracle Java Document Concurrency篇
原文链接如下:https://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html
线程对象
在JAVA中,每个线程都和Thread类有关。为了创建并发的程序,使用Thread对象有两个基本的策略。
- 直接控制线程的创建和管理,每次应用需要一个异步操作时,每次实例化一个Thread对象。
- 把对线程的管理部分从你的应用中抽取出来,然后交给执行器(Executor)去完成。
这个部分只谈论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();
}
}
注意到,两种方式都使用Thread.start方法来启动新的线程。
那么你应该倾向于使用哪种方法呢?第一种方法,构造了一个Runnable对象,是一个更为通用的方法,因为Runnable对象可以承载除了Thread类的其他子类。第二种方法在简单的应用中比较容易实现,但它的局限性是任务类必须是Thread的子类。本教程会集中讲解第一种方法,因为它不仅拥有更好的扩展性,而且也更容易应用高层次的线程管理API。
Thread类中定义了一系列对线程管理有用的方法,包括一些静态的方法,可以提供、影响调用该方法的线程的状态。其他方法是由其他线程发起的,来管理线程和线程对象。我们会在后续的章节看到这些方法。
使用Sleep方法停止执行
Thread.sleep方法会让当前的线程停止运行一段时间。这是一种把处理器时间提供给其他线程的有效方法。sleep方法也能用来作为节奏器,如下的代码所示。
还有两种重载的sleep方法,一个以毫秒为单位具体指出了休眠的时间,另一个以纳秒为单位指定了休眠时间。不过,这些休眠时间并不保证精确,因为被OS之下的机制所限制。此外,休眠过程也可能被中断打扰。总之, 你不能指望调用sleep方法就能准确延迟线程你指定的时间。
下面的SleepMessage类使用sleep方法,以四秒的间隙,来打印信息。
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。这个异常会在当前进程休眠被其他进程打扰时抛出。不过既然当前的方法里只有一个线程,那也就不会导致这个异常。