前言
Java中创建线程的方式主要有以下三种:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
继承Thread类
通过继承Thread类并重写run方法来创建线程,调用start方法来开启线程
// 自定义线程类继承Thread
public class MyThread extends Thread {
// 重写run方法
@Override
public void run() {
System.out.println("自定义线程执行...");
}
public static void main(String[] args) {
new MyThread().start();
}
}
基本步骤为:
- 自定义线程类继承
Thread - 重写
run方法 - 创建线程类并调用
start方法
注意:
- 虽然我们重写了
run方法,并且线程最终也执行了run方法,但是不能直接调用run方法来开启线程,直接调用只会当做普通方法来执行,并不会创建线程。只有调用start方法,执行其中的start0本地方法,才会创建线程。 start方法只能调用一次,重复调用会抛出java.lang.IllegalThreadStateException异常,这个跟线程状态有关,后面文章会详细介绍。
实现Runnable接口
首先查看一下Thread类和Runnable接口源码
// 构造方法
public Thread(Runnable target) { // 1
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// ...省略其他代码
this.target = target; // 2
setPriority(priority);
// ...省略其他代码
}
public void run() {
if (target != null) {
target.run(); // 3
}
}
public interface Runnable {
public abstract void run(); // 4
}
Thread的构造方法可以接收一个Runnable类型参数- 这个
Runnable类型参数赋值给了target属性 Thread类的run方法执行时会先判断target是否为null,不为null的话就执行target的run方法Runnable接口只有一个run方法
所以我们就可以通过实现Runnable接口并实现run方法的方式来创建线程,代码如下:
// 1.实现Runnable接口
public class MyRunnable implements Runnable {
// 2.实现run方法
@Override
public void run() {
System.out.println("通过实现Runnable接口创建线程...");
}
public static void main(String[] args) {
// 3.创建Runnable对象
Runnable runnable = new MyRunnable();
// 4.创建Thread对象并传入Runnable对象参数
Thread thread = new Thread(runnable);
// 5.调用start方法
thread.start();
}
}
通过Java8的lambda可以进行简化
public static void main(String[] args) {
new Thread(() -> {
System.out.println("lambda简化创建线程...");
}).start();
}
实现Callable接口
主要步骤:
- 自定义类实现Callable接口
- 创建FutureTask对象并传入Callable类型参数
- 创建Thread对象并传入FutureTask对象
- 调用Thread类的start方法开启线程
- 调用FutureTask类的get方法获取返回值
代码如下:
public class MyCallable implements Callable { // 1
public static void main(String[] args) {
MyCallable myCallable = new MyCallable(); // 2
FutureTask futureTask = new FutureTask(myCallable); // 2
Thread thread = new Thread(futureTask); // 3
thread.start(); // 4
Object result = null;
try {
result = futureTask.get(); // 5
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(result);
}
@Override
public Object call() throws Exception {
System.out.println("实现Callable创建线程...");
return "success";
}
}
下面通过查看FutureTask和Callable源码查看其执行原理(这里只考虑程序正常执行的情况)
public class FutureTask<V> implements RunnableFuture<V> {} // 0
public interface RunnableFuture<V> extends Runnable, Future<V> {} // 0
public FutureTask(Callable<V> callable) { // 1
if (callable == null)
throw new NullPointerException();
this.callable = callable; // 1
this.state = NEW;
}
public void run() {
// 省略...
try {
Callable<V> c = callable; // 2
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 2
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result); // 3
}
} finally {
// 省略...
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v; // 4
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s); // 5
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x; // 6
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
public interface Callable<V> {
V call() throws Exception; // 7
}
- 通过
FutureTask类的继承关系可以看出其间接继承自Runnable接口,因此可以按照Runnable的方式创建线程; FutureTask构造方法可以接收Callable参数,并赋值给callable属性;run方法中把callable赋值给局部变量c,并调用c的call方法,并且得到一个返回值result;- 调用
set方法处理result; - 最终
result保存到属性outcome属性中; get方法返回的是report方法的结果;report方法最终返回的是属性outcome的值;Callable接口只有一个call方法,并且可以有返回值。
Runnable和Callable的区别
通过两个接口的源码可以看出有以下3种区别:
Callable接口的方法名是call(),Runnable接口中方法名是run();Callable接口中call()方法有返回值,Runnable接口中run()方法没有返回值;Callable接口中call()方法会抛出异常,源码中是这样写的Throws: Exception - if unable to compute a result,也就是说call方法如果无法计算出一个结果就会抛出异常,Runnable接口中run()方法不会抛出异常;