一、线程的创建与对比
关于Java多线程的创建,Oracle文档中是如此描述的:
- 一种是将类声明为Thread的子类。该子类应该重写类的run方法Thread。然后可以分配和启动子类的实例。
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
- 另一种方法是声明实现类Runnable接口。然后实现了run方法。然后可以分配类的实例,在创建时作为参数传递Thread,并启动。
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
那么这两种方法,哪一种更好呢?可从三个方面考虑:
- 基于Java本身只可实现单继承,如果此时使用继承Thread的写法,后续需求更改,需要继承其他类时,变更复杂,而选择实现Runnable接口会更为灵活。
- 从代码架构和解耦的角度来说,Runnable只实现具体的任务,功能比较单一,它可以和线程的生命周期分离解耦。
- 从资源开销方面来讲,每次新建Thread,均需要创建、执行、销毁,开销是比较大的。而用Runnable还可以选择用线程池等,大大减少创建、销毁线程的损耗。
由此看来,用Runnable会更方便些。
二、两种创建方式的源码解析:
当我们同时使用两种方法创建时,例如下列方式
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}){
@Override
public void run() {
//super.run();
System.out.println("我来自Thread");
}
}.start();
}
}
其运行结果是:我来自Thread。看其源码,究其原因:
Thread中的run()方法:
@Override
public void run() {
if (target != null) {
target.run();
}
}
其实是在执行target的run方法,而target呢?Thread中的定义如下:
private Runnable target;
此target是创建Thread时传入的Runnable对象。
可见,如果没有override Thread中的run方法,执行的就是target的run方法,也就是Runnable中的run方法,打印为“我来自Runnable”。但Thread重写了run方法,就不会执行上面三条语句,亦不会判断target对象。而是直接执行了Thread中被override run方法,所以打印出的是"我来自Thread"。
三、一些其他的外在表现形式
以下一些方式,其本质上都是上面两种创建方式的封装:
- 线程池创建线,本质上也是用Thread来创建线程
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0; i< 1000; i++){
executorService.submit(new Task());
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
- 通过Callable和FutureTask创建线程,实质上是实现了Runnable接口
- 定时器,和线程池并没有分别
网友评论