1、通常我们使用的两个接口类:Thread类、Runnable类
(1)Thread类
Java可以通过继承Thread类,并重写run方法来实现我们自己的线程类。然后使用start方法来启动线程。具体可以看以下示例:
public class MyThread extends Thread {
@Override
public void run() {
// todo
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
(2)Runnable类
Java可通过实现Runnable类的run方法来实现我们自己的线程类。使用run方法来启动线程。具体可以看以下示例:
public class MyRunnable implements Runnable {
@Override
public void run() {
// todo
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
runnable.run();
}
}
(3)两种实现方式的优劣势
① java具有“单继承 多实现”的特性,所以Runnable使用起来更加的灵活
② Runnable接口更符合面向对象的特性,将线程单独的进行对象的封装
③ 如果不需要使用Thread类中的诸多方法,显然使用Runnable更加的轻便
但是以上两种实现方式都是没有返回值的,在我们需要返回值的时候,java提供了Callable接口和Future类来实现。
2、具有返回值的Callable接口和Future类
Callable接口与Runnable一样,都具有一个抽象方法,不同的是Callable是有返回值的,并且支持泛型。通常它会与线程池工具ExcutorService一起使用,具体的可以看以下示例:
public class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// TODO
return 1;
}
public static void main(String[] args) {
// 通常是配合线程池ExcutorService来使用
ExcutorService excutor = Excutors.newCachedThreadPool();
Task task = new Task();
// 使用submit方法来开启线程,返回的是Future类
Future<Integer> future = excutor.submit(task);
// 使用Future类的get方法获取线程的返回值
System.out.println(future.get())
}
}
(1)Future类中具有cancel方法,可以试图取消一个线程的执行。但是能不能成功不确定,因为可能线程已经执行完了,也有可能因为某些原因,停止不了。但是如果想要让任务具有取消功能的话,也可以使用Callable接口来替代Runnable接口。
(2)Future只是一个接口,它里面的cancel、get等方法要自己实现起来都是非常复杂的。所以我们可以使用FutureTask类。在高并发的场景下,有可能Callable接口和FutureTask类会被创建很多次,但是FutureTask类能保证任务只执行一次。
public class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// TODO
return 1;
}
public static void main(String[] args) {
ExcutorService excutor = Excutors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
excutor.submit(futureTask);
System.out.println(futureTask.get());
}
}