一、什么是同步和异步
- 同步是阻塞模式,异步是非阻塞模式。
- 同步就是指程序在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会—直等待下去,知道收到返回信息才继续执行下去。
- 异步就是程序调用一个耗时较长的功能(方法)时,它并不会阻塞程序的执行流程,程序会继续往下执行。当功能执行完毕时,程序能够获得执行完毕的消息或能够访问到执行的结果(如果有返回值或需要返回值时)。
下面通过一个示例来看一下同步和异步的区别。示例中程序通过网络获取两个文件,并对两个文件进行合并处理:
二、实现异步的四种方式
1、继承Thread类
public class TestDemo {
public static void main(String[] args) {
System.out.println("main...start....");
new Thread01().start();
System.out.println("main...end....");
}
/**
* 继承Thread
*/
public static class Thread01 extends Thread {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
}
}
}
2、实现Runnable接口
public class TestDemo {
public static void main(String[] args) {
System.out.println("main...start....");
new Thread(new Runnable01()).start();
System.out.println("main...end....");
}
/**
* 实现Runnable接口
*/
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
}
}
}
3、实现Callable接口
public class TestDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start....");
new Thread(new Callable01()).start();
System.out.println("main...end...." );
}
/**
* 实现Callable接口
*/
public static class Callable01 implements Callable<Integer> {
@Override
public void call() throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getId());
return 0;
}
}
}
4、线程池
1、通过Executors工具类创建固定的线程池,例如:
// 带缓存的线程池,可以灵活回收线程
ExecutorService pool1 = Executors.newCachedThreadPool();
// 固定大小的线程池
ExecutorService pool2 = Executors.newFixedThreadPool(10);
// 定时任务的线程池
ScheduledExecutorService pool3 = Executors.newScheduledThreadPool(10);
// 单线程的线程池,从队列中获取任务挨个执行
ExecutorService pool4 = Executors.newSingleThreadExecutor();
2、通过原生的方式创建线程池:(推荐)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
//核心线程数,线程池创建好以后准备就绪的线程数
5,
//最大线程数,控制资源
200,
//存活时间,线程空闲时间超过设定的值,该线程就会被释放
10,
//时间单位
TimeUnit.SECONDS,
//阻塞队列,将多余的任务放在队列中等待执行
new LinkedBlockingQueue<>(),
//线程的创建工厂
Executors.defaultThreadFactory(),
//当队列满了后,按照指定的拒绝策略拒绝执行任务
new ThreadPoolExecutor.AbortPolicy());
三、获取异步任务的返回值
1、Future
a、基于@Async的有返回值的异步方法
@Async
public Future<String> asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - " + Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world !!!!");
} catch (InterruptedException e) {
//
}
return null;
}
b、 获取上述方法的返回值
public void testAsyncAnnotationForMethodsWithReturnType()
throws InterruptedException, ExecutionException {
System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName());
Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
while (true) { ///这里使用了循环判断,等待获取结果信息
if (future.isDone()) { //判断是否执行完毕
System.out.println("Result from asynchronous process - " + future.get());
break;
}
System.out.println("Continue doing something else. ");
Thread.sleep(1000);
}
}
2、CompletableFuture异步编排
Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在Java8之前,只有无Callback 的Future)。
CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。
CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理
详情见:Java8 CompletableFuture 用法全解、CompletableFuture使用详解