Java创建线程的四种方式
- 继承Thread类:浪费仅有的一次继承机会。
- 实现Runnable接口:无返回值。
- 实现Callable接口:可以有返回值,可抛出异常,可中断。
- 通过线程池创建:线程复用,但是实际开发中应该自己实现线程池。
Callable接口通过FutureTask接口的适配
FutureTask是Future、Runnable接口的实现类,其构造函数接收Callable接口参数。
Future接口主要的方法有
- isCancel()
- isDone()
- get()
也正因为Future接口的这些方法,相对于Runnable接口而言,Callable接口的优势在于
- 判断线程任务是否完成或者被取消。
- 能够中断任务。
- 能够获得返回结果,且可以抛出异常。
注意点
- get()方法尽量推迟使用,过早使用可能会阻塞主线程。
- 判断call()方法是否已经结束可以通过while&isDone()方法,类似于自旋锁形式。
- 多个线程同用一个FutureTask对象,对应的call()方法只会被执行一次。
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Enter call() method...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 2571;
}
public static void main(String[] args) throws Exception {
FutureTask<Integer> task = new FutureTask<>(new CallableDemo());
// 注意多个线程使用同一个FutureTask对象,对应的call方法只会被执行一次
new Thread(task, "AAA").start();
new Thread(task, "BBB").start();
Integer res = task.get();
// 过早调用get()方法会导致主线程阻塞
while (!task.isDone()) {
// 类似于自旋锁
}
System.out.println(res + 100);
}
}