一.创建线程方式
面试官:先自我介绍一下。
小面:哔哩吧啦。。。。。
面试官:先介绍一下创建线程的方法吧。
小面:心想"好彩八股文背过"
创建线程分为两种:
- 不需要获取结果,不需要获取到线程的执行结果的。
- 通过继承Thread,重写run方法实现
- 通过实现Runnable接口,需要通过Thread来构造,调用start方法启动
public static void main(String[] args) { SubThread subThread = new SubThread(); subThread.start(); Thread thread = new Thread(new SubThread2()); thread.start(); } //继承Thread方式 private static class SubThread extends Thread{ @Override public void run() { System.out.println("我是小面"); } } //实现Runnable方式 private static class SubThread2 implements Runnable{ @Override public void run() { System.out.println("我是小面2"); } }
- 需要获取结果,有时候我们需要获取到线程的执行结果
通过实现Callable,重写call方法,callable是泛型类,具体类型是返回的结果类型。结果需要借助FutureTask来获取,FutureTask需要放到Thread来启动。另外FutureTask是实现了Future,所以支持cancel,isDone,get这些方法。
public static void main(String[] args) { FutureTask<String> futureTask = new FutureTask<>(new SubThread3()); Thread thread2 = new Thread(futureTask); thread2.start(); try { //获取到结果输出 System.out.println( futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } private static class SubThread3 implements Callable<String>{ @Override public String call() throws Exception { return "我是小面3"; } }
二.等待结果
面试官:不错,那创建了子线程后,主线程怎么等待子线程完成。
比如泡茶的例子,分成三个线程,主线程需要等待两个子线程完成后才能进行,因为需要依赖子线程的结果。
- 主线程:泡茶,喝茶
- 子线程1:洗茶具
- 子线程2:烧水
小面:这个问题之前学习并发编程课程的时候也学习过。
最简单的方式:join方法
在主线程调用子线程1和子线程2的join方法,主线程就会阻塞,一直等到子线程1和子线程2完成后才会运行。
public static void main(String[] args) { SubThread subThread = new SubThread(); subThread.start(); SubThread subThread2 = new SubThread(); subThread2.start(); try { subThread.join(); subThread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "主线程开始泡茶"); } private static class SubThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":开始洗茶具"); try { //模拟洗茶具 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束"); } } private static class SubThread2 extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":烧水开始"); try { //模拟烧水 Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":烧水结束"); } }
获取到返回结果:FutureTask
join方法不能获取到子线程的执行结果,需要获取到返回结果可以通过FutureTask+Callable方式实现
public static void main(String[] args) { FutureTask<Boolean> futureTask1 = new FutureTask<>(new SubCallable()); FutureTask<Boolean> futureTask2 = new FutureTask<>(new SubCallable2()); Thread thread1 = new Thread(futureTask1); thread1.start(); Thread thread2 = new Thread(futureTask2); thread2.start(); while(true){ try { if (futureTask1.get() && futureTask2.get()) { break; } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "主线程开始泡茶"); } private static class SubCallable implements Callable<Boolean> { @Override public Boolean call() throws Exception { System.out.println(Thread.currentThread().getName() + ":开始洗茶具"); try { //模拟洗茶具 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); return false; } System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束"); return true; } } private static class SubCallable2 implements Callable<Boolean> { @Override public Boolean call() throws Exception { System.out.println(Thread.currentThread().getName() + ":烧水开始"); try { //模拟烧水 Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); return false; } System.out.println(Thread.currentThread().getName() + ":烧水结束"); return true; } }
非阻塞方式获取到结果
通过FutureTask只能通过阻塞的方式获取结果,这时候主线程就什么也不能做,有没有一种方式可以不阻塞主线程,获取结果呢?
通过Guava异步任务接口:基于回调的方式实现,需要配合线程池使用。
把线程池放到ListeningExecutorService里,调用submit方法提交callable任务,会返回listenableFuture
Futures.addCallback把listenableFuture和回调函数绑定起来,子线程完成后就会调用回调函数,这样就不需要阻塞主线程了。
需要引入依赖: <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> public static void main(String[] args) throws InterruptedException { final boolean[] subThreadDone = {false, false}; //创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService); ListenableFuture<Boolean> listenableFuture1 = listeningExecutorService.submit(new SubCallable()); ListenableFuture<Boolean> listenableFuture2 = listeningExecutorService.submit(new SubCallable2()); Futures.addCallback(listenableFuture1, new FutureCallback<Boolean>() { @Override public void onSuccess(@Nullable Boolean aBoolean) { subThreadDone[0] = aBoolean; } @Override public void onFailure(Throwable throwable) { subThreadDone[0] = false; } }, executorService); Futures.addCallback(listenableFuture2, new FutureCallback<Boolean>() { @Override public void onSuccess(@Nullable Boolean aBoolean) { subThreadDone[1] = aBoolean; } @Override public void onFailure(Throwable throwable) { subThreadDone[1] = false; } }, executorService); while (true) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + ":看书"); if (subThreadDone[0] && subThreadDone[1]) { System.out.println(Thread.currentThread().getName() + "主线程开始泡茶"); } } } private static class SubCallable implements Callable<Boolean> { @Override public Boolean call() throws Exception { System.out.println(Thread.currentThread().getName() + ":开始洗茶具"); try { //模拟洗茶具 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); return false; } System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束"); return true; } } private static class SubCallable2 implements Callable<Boolean> { @Override public Boolean call() throws Exception { System.out.println(Thread.currentThread().getName() + ":烧水开始"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); return false; } System.out.println(Thread.currentThread().getName() + ":烧水结束"); return true; } }
小结
面试官听完小面的回答,点了点头,说你在这里等一下。想知道小面有没有通过面试,可以持续关注后续文章。
总结一下:
创建线程时:
- 不需要获取结果可以使用Runnable和Thread创建
- 需要获取结果可以使用Callable+FutureTask搭配使用
等待结果时:
- 使用调用线程的join方法,就会一直等待该线程完成,才往下运行
- 通过FutureTask+Callable方式,调用FutureTask的get方法获取结果
- 通过guava工具包,创建listenableFuture回调的方式获取结果,这种方式可以不阻塞线程
你学会了吗?欢迎持续关注!