面试官:线程有几种创建方式?
这个也太简单了叭~ 😝
继承 Thread 类
步骤如下:
- 创建一个类继承
Thread
- 重写
run
方法
- 调用
Thread
类的start
方法
类图~
代码如下
public class MyThread extends Thread{ @Override public void run() { System.out.println(this.getClass().getSimpleName()); } public static void main(String[] args) { MyThread myThread=new MyThread(); myThread.start(); } } 复制代码
实现 Runnable 接口
步骤如下:
- 创建一个类实现
Runnable
- 重写
run
方法
- 将该类传递到
Thread
类的构造函数中
- 调用
Thread
类的start
方法
代码如下
public class MyRunnable implements Runnable{ @Override public void run() { System.out.println(this.getClass().getSimpleName()); } public static void main(String[] args) { Thread myRunable=new Thread(new MyRunnable()); myRunable.start(); } } 复制代码
实现 Callable 接口,结合 Future 类获取返回值
特点:可以获取线程的执行结果
步骤如下:
- 创建一个类实现
Callable
- 重写
call
方法
- 创建
FutureTask
类,将该类传递到FutureTask
类的构造函数中
- 将
FutureTask
类传递到Thread
类的构造函数中
- 调用
Thread
类的start
方法
- 调用
FutureTask
类的get
方法获取线程执行结果
FutureTask
类不知道大家熟不熟悉~ ,这是它的类图😋
可以发现它也是实现了这个 Runnable
接口 ,对比上面发现大家都实现了这个
Runnable
接口 。
然后传参到 Thread
类的构造器中,然后再去调用 start
方法去启动线程~🐷
代码如下
/** * @author Java4ye * @date 2021/4/15 6:58 * @微信公众号: Java4ye * @GitHub https://github.com/RyzeYang * @博客 https://blog.csdn.net/weixin_40251892 */ public class MyCallable implements Callable<String> { @Override public String call() throws Exception { return this.getClass().getSimpleName(); } public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask=new FutureTask<>(new MyCallable()); Thread thread = new Thread(futureTask); thread.start(); System.out.println(futureTask.get()); } } 复制代码
通过线程池创建线程
线程池常见创建的方式有 5 种,具体的讲到 线程池 篇再展开~ 😝
这里的例子使用 固定线程数量的线程池 : newFixedThreadPool
步骤如下:
- 使用 Executors 创建线程池
- 创建一个实现
Runnable
或者Callable
接口的类
- 针对 没有 返回值的,有
execute
方法
- 针对 有 返回值的有 , 有
submit
方法
代码如下
execute 方式
ExecutorService executor = Executors.newFixedThreadPool(1); executor.execute(new MyRunnable()); executor.shutdown(); 复制代码
submit 方式
ExecutorService executor = Executors.newFixedThreadPool(1); Future<String> future = executor.submit(new MyCallable()); System.out.println(future.get()); executor.shutdown(); 复制代码
有几种创建方式呢
嘿嘿 让我数一数 👉 一共有 四 种
事情真的这么简单吗~
当我无意间打开这个 Thread
的源码时,发现它居然说 有两种方式 , 就是前两种方式
虽然官方文档里说两种,但是 咱们和面试官说的时候肯定是说四种呀~ 😋
多多益善 哈哈哈哈 ~
看到这个表情包表示还没结束呢 U•ェ•*U 哈哈哈
还可以和 面试官 说说下面这种异步处理结果的 ,Future
增强版!
CompletableFuture
(上面的 FutureTask
是阻塞版~)
CompletableFuture
为什么又来了一个 Future
呢?
主要是因为用 Future
获得异步执行结果时,要调用 阻塞方法get()
或者轮询
isDone()
判断是否为true
,这两种方法都会导致主线程被 阻塞 ,和我们想的异步不
一样~ (我们需要它再帮我们处理下异步线程的执行结果呀😝)
所以在 JDK1.8
新引入了该类 CompletableFuture
,它实现了 Future
接口 ,又
通过 CompletionStage
接口扩展了功能,增加了异步事件,实现异步处理线程执行完
的结果,不用我们通过 Future
的阻塞方式去手动处理。
除了异步执行结果 外,它还可以 异步处理异常 ,具体看下面的例子😄
类图:
部分方法介绍
supplyAsync
表示创建带返回值的异步任务
runAsync
表示创建无返回值的异步任务
thenAccept
处理返回值,无返回结果
exceptionally
出现异常时,执行 exceptionally
中的回调方法。
正常例子
public class MyCompletableFuture { public static void main(String[] args) { CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Java4ye"); completableFuture.thenAccept(System.out::println); completableFuture.exceptionally(throwable -> { System.out.println(throwable.getMessage()); return throwable.getMessage(); }); } } 复制代码
异常例子
public class MyCompletableFuture { public static void main(String[] args) { CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> ""+1/0); completableFuture.thenAccept(System.out::println); completableFuture.exceptionally(throwable -> { System.out.println(throwable.getMessage()); return throwable.getMessage(); }); } } 复制代码
嘿嘿 后面有机会再单独写一个介绍 CompletableFuture
的 ,里面还有很多方法 😝
总感觉这样结束也太干巴巴了 , 最后再和大家分享一道题 , 嘿嘿 你觉得答案是啥呢?
new Thread(()->{ System.out.println("A"); }){ @Override public void run() { System.out.println("B"); } }.start(); 复制代码
嘿嘿 这里的答案是 B , 看着花里胡哨的,其实是子类重写了父类的 run
方法 ~