原文链接:http://tutorials.jenkov.com/java-concurrency/creating-and-starting-threads.html
和其他的 Java 对象一样, Java 线程也是对象。线程是 Java java.lang.Thread 类或者其子类的实例。除了作为一个普通的对象, Java 线程还可以执行代码。
在 Java 中,可以这样来创建一个线程:
Thread thread = new Thread();
可以通过调用它的 start() 方法来启动线程:
thread.start();
在这个例子中,没有定义线程执行的代码。线程启动后就会立刻停止。
有两种方法来定义线程执行的代码。第一种是创建一个 Thread 子类并重写父类的 run() 方法。调用 start() 方法后,线程会执行 run() 方法。下面是一个例子:
public class MyThread extends Thread {
public void main() {
System.out.println("MyThread running");
}
}
只要线程启动以后, start() 方法调用会立刻返回,不会等到 run() 方法执行完才返回。 run() 方法执行时就像就在另外一个处理器上执行。当 run() 方法运行时会打印出文本“My Thread running”。
也可以像这样来创建一个匿名的 Thread 子类:
Thread thread = new Thread() {
public void run() {
System.out.println("Thread Running");
}
}
thread.start();
在这个例子中,新建的线程一旦执行 run() 方法后将会打印出文本“Thread running”。
指定一个线程执行什么样的代码的第二种方式是创建一个实现了 java.lang.Runnable 接口的类。可以通过一个 Thread 对象来执行 Runnable 对象。
下面是一个 Java Runnable 例子:
public class MyRunnable implements Runnable {
public void run() {
System.out.println("MyRunnable running");
}
}
为了让 run() 方法被一个线程来执行,可以传递一个 MyRunnable 的实例给 Thread 类的构造方法。下面是具体做法:
Thread thread = new Thread(new MyRunnable());
thread.start();
当线程启动以后它将会执行 MyRunnable 实例的 run() 方法,而不会执行自己的 run() 方法。上面的例子中将会打印出文本“MyRunnable running”。
也可以像这样创建一个匿名的 Runnable 实现类:
Runnable myRunnable = new Runnable() {
System.out.println("Runnable runnig");
}
Thread thread = new Thread(myRunnable);
thread.start();
两种方法哪种最好,没有定则,两种方法都可行。然而,我个人更偏爱实现 Runnable 接口,然后传递一个实现类的实例给 Thread 实例。当让 Runnable 的实现类通过一个线程池来执行的时候,来自线程池的一个线程空闲以后 Runnable 的实例更容易排队。用 Thread 的子类通过线程池来执行的话,要比 Runnable 的实现类困难。
有时候必须实现 Runnable 接口同时继承 Thread 类。例如,创建一个可以执行多个 Runnable 的 Thread 子类,这是实现线程池时典型的一种场景。
创建并启动线程线程时一个常见的错误是像下面这样直接调用调用 Thread 的 run() 方法而不是 start() 方法:
Thread newThread = new Thread(MyRunnable));
newThread.run(); // 应该是调用 newThread.start();
起初,可能并没有发现什么异常因为 Runnable 的 run() 方法正如期望的那样执行了。然而, run() 方法并不是被刚才新建的线程执行的,而是被创建这个新线程的线程执行的。换句话说,执行 run() 方法的线程就是执行了以上两行代码的线程。为了让新建的线程 newThread 来执行 MyRunnable 实例的 run() 方法,必须通过调用 newThread.start() 方法来实现。
为了给大家证明这个结论,我写了段代码来重现这个现象。下面是我写的一个 Thread 的子类以及一个 main() 方法。
public class MyThread extends Thread{
public static void main(String[] args) {
// 设置主线程的名字
Thread.currentThread().setName("main thread");
MyThread myThread1 = new MyThread();
myThread1.setName("new thread1");
MyThread myThread2 = new MyThread();
myThread2.setName("new thread2");
myThread1.run();
myThread2.start();
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name);
}
}
打印出来的结果是:
main thread
new thread2
由此可以证明调用 myThread1.run() 以后,执行 run() 方法的的确是 main 线程(即创建 myThread1 线程的线程)。
创建了一个线程以后可以给该线程起一个名字。这个名字可以帮助我们区别出不同的线程。例如,多个线程都通过 System.out 打印了文本,通过线程名字我们可以很方便的的看出是哪个线程打印的文本。下面是一个例子:
Thread thread = new Thread("New Thread") {
public void run() {
System.out.println("run by" + getName());
}
};
thread.start();
System.out.println(thread.getName);
注意字符串 “New Thread” 作为一个参数被传递给 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();
因此可以通过下面的方式来获取当前执行线程的名字:
String threadName = Thread.currentThread().getName();
下面是一个小例子。首先会打印出执行 main() 方法的线程的线程名字。该线程是由 JVM 分配的。然后,该线程会启动 10 个线程,给它们一个数字作为线程的名字。每个线程然后打印出自己的线程名字,最后执行完毕。
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();
}
}
}
注意,即使线程启动的顺序是(0,1,2,3,4等)但它们执行时可能不是顺序的,线程 0 可能不是第一个通过 System.out 打印出线程名字的线程。这是因为线程在原则上是并行执行而不是顺序执行。 JVM 或/和操作系统决定了线程执行的顺序。线程执行的顺序和它们的启动顺序没有必然的关联。
Next: 竞争条件和临界区