Start开启线程源码分析
- Thread.start开启线程
java">public class JUC01 {public static void main(String[] args) {Thread t1 = new Thread(() -> System.out.println("开启线程:"+Thread.currentThread().getName()),"t1");t1.start();}
}
- start方法源码
java"> public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionality added* to this method in the future may have to also be added to the VM.** A zero status value corresponds to state "NEW".*/if (threadStatus != 0)throw new IllegalThreadStateException();/* Notify the group that this thread is about to be started* so that it can be added to the group's list of threads* and the group's unstarted count can be decremented. */group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}}
- start方法中调用start0方法,start0方法是native方法,底层使用c++编写
java">private native void start0();
总结:
java线程是通过start的方法启动执行的,主要内容在native方法start0中,openjdk的写JNI一般是一一对应的,Thread.java对应的就是Thread.c,start0其实就是JVM_StartThread。此时查看源代码可以看到在jvm.h中找到了声明,jvm.cpp中有实现。
jvm配合操作系统,底层由操作系统分配了一个原生的基础线程。
Future接口
Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个很耗时的计算任务,我们就可以通过Future把这个任务放到异步线程中执行。主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。
FutureTask类
FutureTask实现了RunnableFuture接口,RunnableFuture接口实现了Runnable接口和Future接口,FutureTask有一个构造方法:FutureTask(Callable callable)
实现Runnable接口后,可以使用new Thread(Runnable r)开启多线程;
Future接口中有可以取消已启动的线程的方法;
FutureTask的构造方法FutureTask(Callable callable)使用构造注入的方式将Callable为自身所用,可以使用Callable接口的call方法编写多线程的逻辑,且call方法有返回值。
实例:
FutureTask的缺点:
- get方法容易阻塞
一般建议将get方法放在程序后面,假如不愿意等待很长时间,可以使用get(long timeout, TimeUnit unit)设置超时自动离开。
- isDone轮询会耗费无谓的cpu资源,而且也不见得能及时地得到计算结果。
结论:Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。
CompletableFuture类
get方法阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的CPU资源。因此,
JDK8设计出CompletableFuture。
CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。
在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture的方法。它可能代表一个明确完成的Future,也有可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作。
java">public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {}
CompletionStage接口:
CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数:
- CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段
- 一个阶段的计算执行可以是一个Function,Consumer或Runnable。 比如:
stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.printin())
- 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发
四个静态方法
尽量不要使用构造方法获得CompletableFuture对象,尽量使用四个静态方法获取。
- 返回值为Void
java">public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
- 返回值为泛型U
java">public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
参数:
- Executor:
没有传入指定的Executor,直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码
如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
- Runnable:
传入Runnable的两个方法均无返回值
- Supplier
供给型函数接口
实例:
runAsync方法不传入线程池
runAsync方法传入线程池
supplyAsync方法不传入线程池
CompletableFuture的使用
从Java8开始引入了CompletableFuture,它是Future的功能增强版,减少阻塞和轮询,可以传入回调对象,当异步务完成或者发生异常时,自动调用回调对象的回调方法。
实例:
java">import java.util.concurrent.*;public class JUC01 {public static void main(String[] args) {//创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(3);try {CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "------come in");int result = ThreadLocalRandom.current().nextInt(10);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------1秒钟后出结果" + result);return result;}, threadPool)//当上一步(supplyAsync)运行结束时执行whenComplete分支,v表示上一步返回的结果,e表示上一步出现的异常//注意:无论上一步(supplyAsync)是否出现异常,都会执行whenComplete分支.whenComplete((v, e) -> {if (e == null) { //上一步不出现异常时执行System.out.println("------计算完成,更新系统UpdateValue:" + v);}})//当上一步(supplyAsync)出现异常时执行exceptionally分支,e表示上一步出现的异常.exceptionally(e -> {e.printStackTrace();System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());return null;});System.out.println(Thread.currentThread().getName()+"线程先去往其他任务");}catch (Exception e){e.printStackTrace();}finally {//关闭线程池threadPool.shutdown();}}
}
注意:使用默认的线程池时,会把CompletableFuture.supplyAsync产生的线程设置为守护线程,当main线程结束时,守护线程也会随之结束。