目录
一、概述
在Java中,线程使用Thread关键字来表示,所有线程对象,都必须来自Thread类或者Thread类子类的实例。
1.Thread类的继承关系
从上图中可以看出Thread类实现了Runnable接口。
2.Thread类的构造方法总览
下面所有的构造方法均按其在源码中的顺序排列。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
3.Thread类常用的构造方法
//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, null, gname) ,其中gname是新生成的名字。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, null, name) 。 name为新线程的名称。
public Thread(String name) {
init(null, null, name, 0);
}
//分配一个新的Thread对象。 该构造函数具有与Thread (null, target, gname)相同的效果,其中gname是新生成的名称。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。target - 启动此线程时调用其run方法的对象。 如果null ,这个类run方法什么都不做。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, target, name)。target为启动此线程时调用其run方法的对象。 如果null ,则调用此线程的run方法。name为新线程的名称
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (group, null, name)。
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
//分配一个新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组。
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
二、线程的创建
1.继承Thread类,创建线程
通过继承Thread类来创建一个线程的一般步骤:
- ①:定义一个类,继承Thread类;
- ②:重写Thread类的run()方法,并将该线程要执行的操作声明在该方法中;
- ③:创建该Thread子类对象的实例;
- ④:调用start()方法,启动该线程。
//第一步:创建一个叫MyThread的类继承于Thread类
class MyThread extends Thread {
//第二步:重写Thread类的run()方法
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); //执行的操作就是打印该线程的名称
}
}
//测试类
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//第三步:创建该Thread子类对象的实例
MyThread myThread1 = new MyThread();
//第四步:调用该线程的start()方法,启动该线程
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.start();
}
}
执行结果:
Thread-0
Thread-1
2.实现Runnable接口,创建线程
通过实现Runnable接口创建一个线程的步骤:
- ①:定义一个类,实现Runnable接口;
- ②:重写其run()方法,跟Thread类中的run()方法一样,将线程要执行的操作声明在该run()方法中;
- ③:创建该Runnable实现类的实例;
- ④:将该实例作为Thread的target来创建Thread对象,这个Thread对象即真正的线程对象;
- ⑤:调用start()方法,启动该线程。
//第一步:创建Runnable接口的实现类MyThread
class MyThread implements Runnable {
//第二步:重写run()方法
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); //执行的操作就是打印该线程的名称
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//第三步:创建Runnble()实现类的实例
MyThread myThread1 = new MyThread();
//第四步:将该实例传到Thread类的构造器中创建Thread对象
Thread thread1 = new Thread(myThread1);
//第五步:调用start()方法启动线程
thread1.start();
Thread thread2 = new Thread(myThread1);
thread2.start();
}
}
执行结果:
Thread-0
Thread-1
3.使用Callable和Future,创建线程
Callable的不同之处在于,与Runnable接口相比,Callable接口提供了一个call( )方法作为线程执行体,call()方法可以有返回值,可以声明抛出异常。
而Future接口则是来代表Callable接口里call()方法的返回值,并且Future接口还有一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。
使用Callable和Future,创建线程的步骤:
- ①:定义一个类,实现Callable接口;
- ②:重写其call()方法,跟上面重写run()方法一样,将线程要执行的操作声明在call()方法中;
- ③:创建该Callable实现类的实例;
- ④:将此对象传递到FuterTask构造器中,创建FuterTask 对象;
- ⑤:将FutureTask对象作为Thread对象的target来创建线程(因为FutureTask实现了Runnable接口);
- ⑥:调用start()方法,启动线程。
- 注意:可以调用FutureTask对象的get()方法来获得线程执行结束后的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//第一步:创建Callable( )接口的实现类
class MyThread implements Callable {
//第二步:重写其call()方法
@Override
public Object call() throws Exception { //线程执行的操作就是求5的阶乘
int sum = 1;
for (int i = 5; i >= 1; i--) {
sum *= i;
}
return sum;
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//第三步:创建该Callable实现类的实例
MyThread myThread = new MyThread();
//第四步:将此对象传递到FuterTask构造器中,创建FuterTask 对象
FutureTask futureTask = new FutureTask(myThread);
//第五步:将FutureTask对象作为Thread对象的target来创建线程
Thread thread1 = new Thread(futureTask);
//第六步:调用start()方法,启动线程
thread1.start();
//获取线程的返回值
try {
System.out.println("5的阶乘结果为:" + futureTask.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
打印结果:
5的阶乘结果为:120
三、三种创建方式的比较
1.前两种方式比较
- 相同之处:都需要重写run( )方法,并将线程执行的操作声明在run( )方法中;
- 不同之处:第一种方式是继承Thread类,由于Java语言单继承的原因,如果它继承了Thread类,那么就没办法继承其它的类了。而第二种方式只是实现了Runnble( )接口,它还可以继续继承其他类。所以往往第二种在实际应用中更加广泛。
2.第三种方式与前两种方式的比较
第三种方式与前两种相比,它实现了Callable接口,并将线程的操作声明在其重写的call()方法中,而不是run()方法,并且在call()方法中可以有返回值,它还可以抛出异常。