Thread类和Runnable接口
JDK提供了Thread类和Runnable接口供我们实现线程。
- 继承Thread类,重写
run()方法 - 实现Runnable接口的
run()方法
继承Thread类
继承Thread类之后,调用start()方法才算启动了线程。调用start()方法之后,虚拟机会创建一个线程,然后等到这个线程第一次得到时间片时再调用实现的run()方法。
public class Demo {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
实现Runnable接口
Runnbale是一个函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Java中真正能创建新线程的只有Thread类对象,通过实现Runnable的方式,最终也时还是通过Thread类对象创建线程并启动。
public class Demo {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
// Java 8 函数式编程,可以省略MyThread类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
Thread类和Runnable接口的比较
- 由于Java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活。
- Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
- Runnable接口出现,降低了线程对象和线程任务的耦合性。
- 如果使用线程时不需要使用Thread类的诸多方法,使用Runnable接口更为轻量。
Callable、Future和FutureTask
使用Runnable和Thread创建新的线程有一个弊端,就是run()方法没有返回值。JDK提供了Callable和Future类解决线程获取返回值的问题,也就是“异步模型”。
Callable接口
Callable也是只有一个抽象方法的函数式接口,Callable提供的call()方法是有返回值的,而且支持泛型。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Callable一般配合线程池工具ExecutorService来使用,ExecutorService可以使用submit()方法来让一个Callable接口执行,它会返回一个Future,后续的程序可以通过这个Future的get方法得到线程的返回结果。
public class Demo {
// 自定义Callable
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 模拟计算需要一秒
Thread.sleep(1000);
return 2;
}
}
public static void main(String args[]) throws ExecutionException, InterruptedException, TimeoutException {
// 线程池
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
// 调用get方法会阻塞当前线程,直到得到结果。
System.out.println(result.get());
// 使用带有时间的get方法可以设置超时时间
// System.out.println(result.get(500, TimeUnit.MILLISECONDS));
}
}
Future接口
Future接口只有几个比较简单的方法。
public abstract interface Future<V> {
// 试图取消线程执行
public abstract boolean cancel(boolean paramBoolean);
// 判断线程在正常结束之前是否有被取消
public abstract boolean isCancelled();
// 判断线程是否结束
public abstract boolean isDone();
public abstract V get() throws InterruptedException, ExecutionException;
public abstract V get(long paramLong, TimeUnit paramTimeUnit)
throws InterruptedException, ExecutionException, TimeoutException;
}
如果让任务有能够取消的功能,可以使用Callable代替Runnable,如果为了线程可取消性而使用Future但又不对外提供结果,则可以声明为Future<?>形式类型,并返回null作为返回结果。
FutureTask类
FutureTask是一个JDK提供的实现了Future上诉方法的一个类,FutureTask实现了RunnableFuture接口,而RunnableFuture接口同时继承了Runnable接口和Future接口,并且有一个抽象方法run()。
public class Demo {
static class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟计算需要一秒
Thread.sleep(1000);
return 2;
}
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
// 线程池
ExecutorService executor = Executors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
executor.submit(futureTask);
System.out.println(futureTask.get());
}
}
该方式是使用submit(Runnable task)提交的线程任务,而在Callable中提交任务是使用submit(Callable<T> task)。并且该方式获取返回值时,可以直接从FutureTask对象中获取。