本人菜鸡一只,前段时间遇到了这个问题,现在做一下总结。
先来看创建线程的两种方式:
Runnable
需要重写run方法,需要注意的是run方法没有返回值,那么主动获取就为空。
Callable
重写的是call方法,这个接口是为了获取返回值,与runnable相比支持泛型。
具体实现
JDK为我们提供了Callable接口和Future接口为我们解决这个问题,也就是所谓的“异步模型”。中心思想是通过线程池执行线程,它会返回一个Future,通过Futured的get方法得到返回结果。
下面是第一个例子:
class TestThread implements Callable{
@Override
public String call() throws Exception {
return "这是返回信息";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建一个线程池,很多种方法就随便用一个,目的不在研究线程池
ExecutorService executor= Executors.newCachedThreadPool();
TestThread testThread = new TestThread();
//调用submit方法来执行线程能够拿到返回值,
//execute不能获取返回值,并且限制上限runnable
Future<String> submit = executor.submit(testThread);
// 注意调⽤get⽅法会阻塞当前线程,直到得到结果。
// 所以实际编码中建议使⽤可以设置超时时间的重载get⽅法。
System.out.println(submit.get());
//注意局部线程池记得关闭,否则会一直运行,
//执行这个方法,线程池会等待阻塞队列中的任务完成,
//这个时候线程池是stop状态,不接受新的任务
//当所有任务终止,会执行terminated()也就是线程池终结
executor.shutdown();
}
}
还可以使用FutureTask这个类同时实现了Future、Runnable接口,将线程又进行了一次封装,有什么意义呢?emm在高并发情况下可能会创建多个Callable和FutureTask,FutureTask能确保任务只执行一次(至于为什么看源码吧~~菜鸡就不瞎逼逼了),下面是实现例子:
class TestThread implements Callable {
@Override
public String call() throws Exception {
return "这是返回信息";
}
public static void main(String[] args) {
//创建线程池
ExecutorService executor = Executors.newCachedThreadPool();
//做了一层封装,使用FutureTask这个Future和runnable接口的实现类
FutureTask<Integer> futureTask = new FutureTask<>(new TestThread());
executor.submit(futureTask);
//同样的get方法会阻塞当前线程
System.out.println(futureTask.get());
//注意局部线程池记得关闭,否则会一直运行
executor.shutdown();
}
}
另外说一些,如果创建的线程实现的是runnble,返回值就是零。个人感jio最关键的就是get方法会阻塞当前线程,如果不太注意会引起问题。