一、创建线程
在Java中创建线程是一件十分轻松的事情,有两种较为常见的线程创建方法,一种是通过继承java.lang.Thread类,另一种则是实现Java.lang.Runnable接口。代码结构如下:
/**
* 继承java.lang.Thread类实现线程
*/
public class MyThread1 extends Thread{
@Override
public void run(){
// ...
}
}
/**
* 实现java.lang.Runnable接口实现线程
*/
public class MyThread2 implements Runnable{
@Override
public void run(){
// ...
}
}
实际上,Thread也是实现了Runable接口的。同时,Thread类还持有一个Runnable类型的成员变量target。通过源码可以发现,Thread实例运行的是其target成员变量的run方法:
@Override
public void run() {
if (target != null) {
target.run();
}
}
对于一个线程,如果只是希望通过重写run方法,执行某些任务,Java的建议是使用实现Runnable接口的方式创建线程。这是有道理的,Java不支持多继承,如果我们通过继承Thread类实现线程,那么这个类就不能再继承其它父类了。同时,使用实现接口的方式也符合多聚合、少继承的面向对象编程原则。
虽然两种方式都能创建一个线程类,但要实例化一个线程,这两种实现也会有一点区别:
// 通过继承Thread类实现的线程,因为其本身也是线程,所以可以直接实例化
Thread t1 = new MyThread1();
// 通过实现Runnable接口实现的线程,只能作为Thread类的参数,赋值给Thread的target成员变量实例化线程
Thread t2 = new Thread(new MyThread2());
二、启动线程
虽然创建线程后,业务处理的代码集中在run方法中,但调用run方法并不能启动一个线程:
t1.run(); // 错误的线程启动方法
上面这行代码也能正确地执行run方法,不过这和任何一个类的实例调用其成员方法没有区别,也就无法到达线程执行的效果。实际上Thread还有一个成员方法start,它的作用就是启动线程。start方法内部执行了另一个native方法start0,这是一个本地方法,所以无法查看其源代码。
t1.start(); // 正确的线程启动方法
虽然线程是通过调用start方法启动线程,但这个方法也会使线程执行run方法中的代码段。