一、同步 vs 异步
概念:
同步: 任务一个个做,做完一个才能做下一个。比如烧水--->洗杯子--->泡茶,必须等水烧开才能泡;
异步: 任务同时进行。烧水的同时可以洗杯子,水烧好后再回来泡茶;
Java同步示例(阻塞):
java
/**
* Java同步
*/
public class SyncExample {
public static void main(String[] args) {
System.out.println("开始准备煮饭");
// 洗米需要3s(模拟)
washRice();
// 煮饭需要20秒(模拟)
cookRice();
System.out.println("饭煮好了,可以吃饭了!");
}
// 洗米操作,需要3秒
static void washRice() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("洗完米");
}
// 煮饭操作,需要20秒
static void cookRice() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
}
System.out.println("饭熟了");
}
}
//开始煮饭------洗完米------饭熟了------饭煮好了,可以吃饭了!(期间啥也做不了)
Java异步简单示例:(用新线程)
java
/**
* Java异步
*/
public class AsyncExample {
public static void main(String[] args) {
System.out.println("开始准备煮饭");
// 异步煮饭:新线程去做,主线程继续
new Thread(() -> {
cookRice(); //耗时操作
System.out.println("饭熟了");
}).start();
// 主线程继续做其他事
washRice();
System.out.println("洗完米,我可以先炒菜...");
// 防止主线程提前结束
try {
Thread.sleep(25000);
} catch (InterruptedException e) {
}
}
// 洗米
static void washRice() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("洗完米");
}
// 做饭操作
static void cookRice() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
}
System.out.println("饭熟了(异步)");
}
}
关键点: 异步不阻塞主线程,能同时做多件事;
二、基础异步工具
概念:
- Thread: 线程,就是一个执行路径;
- Runnable: 要执行的任务(函数式接口);
创建异步任务的三种方式:
java
/**
* 方式1:继承Thread
*/
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("任务1执行中。。。。。。");
}
}
/**
* 方式2:实现Runnable接口(推荐)
*/
class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("任务2执行中。。。。。。");
}
}
//测试类
class Test {
public static void main(String[] args) {
// 方式1:继承Thread
new MyThread().start();
// 方式2:实现Runnable接口
MyThread2 task = new MyThread2();
new Thread(task).start();
// 方式3:匿名内部类
new Thread(() -> {
System.out.println("任务3执行中。。。。。。");
}).start();
}
}
实战:模拟下载文件后处理
java
/**
* 模拟下载文件后处理
*/
public class DownloadExample {
public static void main(String[] args) {
System.out.println("开始下载文件");
//异步下载
new Thread(() -> {
downloadFile("大文件.zip");
//下载完后处理(注意:这还在异步线程里)
processFile("大文件.zip");
}).start();
System.out.println("下载已开始,我去做别的事了");
doOtherWork();
}
static void downloadFile(String file) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(file + "下载完成");
}
static void processFile(String file) {
System.out.println("开始处理" + file);
}
static void doOtherWork() {
System.out.println("浏览网页");
}
}
返回结果:
bash
开始下载文件
下载已开始,我去做别的事了
浏览网页
大文件.zip下载完成
开始处理大文件.zip
痛点: 如果需要在下载完成后把结果返回给主线程,用Thread很难做到。
三、Future --- 获取异步结果
概念:
Future是一个凭证,代表未来的某个结果。你可以:
- 继续做其他事;
- 需要结果时,调用
get()等待异步任务完成并取回结果;
基本使用:
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureExample {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交一个异步任务,返回Future
Future<Integer> future = executor.submit(() -> {
Thread.sleep(2000); //模拟计算
return 100 + 200;
});
System.out.println("异步计算已提交,我先去做别的事");
Thread.sleep(1000);
// 需要结果时,get()会阻塞直到结果准备好
Integer result = future.get(); //这里会的古代最多2秒
System.out.println("结果是:" + result);
executor.shutdown();
}
}
Future的局限(为什么不够好)
java
// 问题1:get()会阻塞线程,失去了异步的意义
Integer result = future.get(); // 等着吧,做不了别的事
// 问题2:无法链式操作(做完后自动触发另一个任务)
// 比如:下载文件 -> 自动解压 -> 自动删除原文件,很难用Future串联
// 问题3:多个异步任务组合很麻烦
四、CompletableFuture ---真正的异步神器
概念:
CompletableFuture实现了Future,但可以非阻塞地链式调用,像流水一样组合异步任务。
4.1 创建异步任务
java
import java.util.concurrent.CompletableFuture;
public class CompletableFutureBasic {
public static void main(String[] args) throws Exception {
// 方式1:runAsync - 无返回值
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
System.out.println("异步执行,无返回值");
});
// 方式2:supplyAsync - 有返回值
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello,Async!";
});
// 获取结果(会阻塞,但通常不用,而是用thenApply等)
System.out.println(future2.get());
// 避免主线程退出
Thread.sleep(1000);
}
}
返回结果:
bash
异步执行,无返回值
Hello,Async!
4.2 thenApply - 转换结果
java
import java.util.concurrent.CompletableFuture;
public class CompletableFutureBasic {
public static void main(String[] args) throws Exception {
CompletableFuture.supplyAsync(() -> {
return "hello";
}).thenApply(result -> {
return result.toUpperCase();
}).thenAccept(finalResult -> {
System.out.println(finalResult); //输出:HELLO
});
}
}
4.3 thenAccept - 消费结果(无返回值)
java
import java.util.concurrent.CompletableFuture;
public class ThenTest {
public static void main(String[] args) {
// thenAccept - 消费结果(无返回值)
CompletableFuture.supplyAsync(() -> 100)
.thenApply(x -> x * 2)
.thenAccept(result -> System.out.println("结果是:" + result)); //结果是:200
}
}
4.4 thenRun - 完成任务后执行(不关心结果)
java
import java.util.concurrent.CompletableFuture;
public class ThenTest {
public static void main(String[] args) {
// thenRun - 完成任务后执行(不关心结果)
CompletableFuture.supplyAsync(() -> "数据保存成功")
.thenRun(() -> System.out.println("发送通知邮件")); //发送通知邮件
}
}
五、 组合多个异步任务
5.1 thenCompose - 任务依赖(前一个结果作为输入)
java
import java.util.concurrent.CompletableFuture;
public class ThenTest02 {
public static void main(String[] args) {
// 场景:根据用户ID获取用户详情(两步都异步)
CompletableFuture.supplyAsync(() -> "user123")
.thenCompose(userId -> getUserDetail(userId)) // 扁平化,避免Future嵌套
.thenAccept(user -> System.out.println(user));
}
static CompletableFuture<String> getUserDetail(String userId) {
return CompletableFuture.supplyAsync(() -> userId + " 的详细信息");
}
}
5.2 thenCombine - 合并两个独立任务的结果
java
import java.util.concurrent.CompletableFuture;
public class ThenTest02 {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
future1.thenCombine(future2, (result1, result2) -> result1 + result2)
.thenAccept(sum -> System.out.println("和是:" + sum)); // 输出 30
}
}
5.3 allOf / anyOf - 等待多个任务
java
import java.util.concurrent.CompletableFuture;
public class ThenTest02 {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "任务2");
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "任务3");
// 等待所有任务完成
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
all.thenRun(() -> System.out.println("所有任务完成"));
// 等待任意一个任务完成
CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2, f3);
any.thenAccept(result -> System.out.println("最先完成的任务结果:" + result));
}
}
六、异常处理
6.1 exceptionally - 处理异常并返回默认值
java
import java.util.concurrent.CompletableFuture;
public class ThenTest03 {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("出错了");
return "成功";
}).exceptionally(ex -> {
System.out.println("捕获异常:" + ex.getMessage());
return "默认值";
}).thenAccept(System.out::println);
}
}
6.2 handle - 无论成功或失败都处理
java
import java.util.concurrent.CompletableFuture;
public class ThenTest03 {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "hello")
.handle((result, ex) -> {
if (ex != null) {
return "错误:" + ex.getMessage();
}
return result.toUpperCase();
})
.thenAccept(System.out::println);
}
}
七、实战综合案例
场景: 从两个服务异步获取用户信息和订单信息,组合后计算总金额,最后发送通知。
java
/**
* 从两个服务异步获取用户信息和订单信息,组合后计算总金额,最后发送通知。
*/
public class RealWorldExample {
public static void main(String[] args) {
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo("123"));
CompletableFuture<List<Order>> orderFuture = CompletableFuture.supplyAsync(() -> getOrders("123"));
userFuture.thenCombine(orderFuture, (user, orders) -> { //合并两个独立任务的结果
double total = orders.stream().mapToDouble(Order::getAmount).sum();
return new Bill(user, total);
}).thenApply(bill -> { //转换结果
System.out.println("生成账单:" + bill);
return bill;
}).thenAccept(bill -> { //消费结果(无返回值)
sendEmail(bill);
}).exceptionally(ex -> {
System.err.println("处理失败:" + ex);
return null;
});
// 防止主线程退出
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
static User getUserInfo(String id) {
sleep(1000);
return new User(id, "张三");
}
static List<Order> getOrders(String userId) {
sleep(1200);
return Arrays.asList(new Order(100), new Order(50));
}
static void sendEmail(Bill bill) {
System.out.println("发送账单邮件给:" + bill.user.name);
}
static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
}
}
}
class User {
String id, name;
User(String id, String name) {
this.id = id;
this.name = name;
}
}
class Order {
double amount;
Order(double amount) {
this.amount = amount;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
class Bill {
User user;
double total;
Bill(User user, double total) {
this.user = user;
this.total = total;
}
}
八、思维导图
