新日撸java三百行新手小白java学习记录
Day1 模拟多线程回调机制
文章目录
- 新日撸java三百行` 新手小白java学习记录 `
- 前言
- 一 、模拟异步机制
- 提出问题
- 解决方案
前言
古人称长江为江,黄河为河。长江水清,黄河水浊,长江在流,黄河也在流。 长江之水灌溉了两岸数省之田地,黄河之水也灌溉了数省两岸之田地,只能不因水清而偏用,也只能不因水浊而偏废,自古皆然。
一 、模拟异步机制
提出问题
众所周知,java实现多线程可以实现Runnable、Callable两个接口,两者区别在于有无返回值。
目标:探究Callable的返回值如何返回的
问题:主线程中派生一个线程A执行有返回值的任务,如何解决主子线程时间差。
解决方案
使用一个已知的对象来承接返回值,命名为MyCompletableFuture。
- 1.定义自已的任务实现接口myable类似Callable接口
public interface Myable<T> {
T my();
}
- 2.创建MyCompletableFuture类
核心类,目的是截断子线程的返回值,使用该类代替子线程的返回值。
大概逻辑:持有任务接口(符合里式替换原则),持有任务的返回值result, 持有标志位isComplete判断任务是否完成,持有还有一个自定义回调接口CallBack。
– get()方法,调用该方法的线程会尝试获取任务的结果,此时有两种情况:任务完成和未完成。
– submit()方法开启一个线程,调用任务接口的my方法执行任务并将返回值赋值给result,完成后调用Complete()方法
– Complete()方法将标志位变成true标明任务完成;唤醒等待中的线程;调用回调。
public class MyCompletableFuture<T> {
private Myable<T> myable;
private T result;
private Exception exception;
private boolean isComplete = false;
// private List<Function<T , ?>> callbacks = new ArrayList();
private List<CallBack<T , ?>> callbacks = new ArrayList();
public MyCompletableFuture() {
}
public MyCompletableFuture(Myable<T> myable) {
this.myable = myable;
}
/**
* 线程的启动方法 , 运行一个Myable的实现类 , 并接受返回值复制给当前类属性 , 而后调用Complete()方法。
*/
public void submit(){
new Thread(
()->{
this.result = myable.my();
try {
Complete();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
).start();
}
/**
* 当此方法被调用,说明返回值已经被成功接收 , 届时可以调整状态属性 , 唤醒调用get()的其他线程 , 执行回调。
* @throws Exception
*/
private synchronized void Complete() throws Exception {
// this.result = result;
isComplete = true;
notifyAll();
executeCallbacks();
}
public synchronized void completeExceptionally (Exception e){
this.exception = e;
this.isComplete = true;
notifyAll();
executeCallbacks();
}
/**
* 判断返回值是否就绪 , 如果就绪则调用列表中的订阅者的回调函数 , 否则订阅该列表。
* @param callback : 实现CallBack接口的回调方法
* @return
*/
public synchronized MyCompletableFuture<Void> thenApply(CallBack<T , ?> callback){
if(isComplete){
if(exception != null){
throw new RuntimeException(exception);
}
callback.callBack(result);
}
else {
callbacks.add(callback);
}
return new MyCompletableFuture<>();
}
/**
* 发布订阅模式执行回调。
*/
public synchronized void executeCallbacks(){
for (CallBack<T, ?> callback: callbacks
) {
callback.callBack(result);
}
callbacks.clear();
}
/**
* 获取返回值的方法,如果准备就绪则获取,否则wait();
* @return
* @throws Exception
*/
public synchronized T get() throws Exception{
if(!isComplete){
wait();
}
if(exception != null){
throw exception;
}
return result;
}
}
- 3.回调实现逻辑
– 定义回调接口CallBack(函数式编程接口)
public interface CallBack<T , R> {
R callBack(T t);
}
– 使用发布订阅模式在MyCompletableFuture中持有了一个CallBack类型的List,实现一个注册方法thenApply,当需要子线程结果的时候就调用thenApply方法
- 4.使用案例
定义任务类实现任务接口,任务就是对一个数组排序
class AsyncProcessor implements Myable<int[]>{
int nums[] ;
public AsyncProcessor(int[] nums) {
this.nums = nums;
}
@Override
public int[] my() {
try {
Thread.sleep(1000);
System.out.println("耗时排序中.......");
Thread.sleep(2000);
Arrays.sort(nums);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return nums;
}
}
主函数 将任务交给MyCompletableFuture
public class Main {
public static void main(String[] args) throws Exception {
int nums[] = new int[]{1,5,7,8,1,5,78,1,5,1};
AsyncProcessor processor = new AsyncProcessor(nums);
MyCompletableFuture<int[]> futureResult = new MyCompletableFuture<>(processor);
futureResult.submit();
futureResult.thenApply(result -> {
System.out.println("最大值: " + result[nums.length-1]);
return null; // 返回值为 void
});
futureResult.thenApply(result -> {
System.out.println("最小值: " + result[0]);
return null; // 返回值为 void
});
futureResult.thenApply(result -> {
System.out.println("排序结果: " + Arrays.toString(result));
return null; // 返回值为 void
});
// 在主线程继续执行其他操作
System.out.println("请求已发送,等待处理结果...");
// 获取结果(会阻塞直到完成)
try {
futureResult.get(); // 获取处理结果,等待完成
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结:主线程想要不阻塞继续执行,必然会有一个已知的类型对象来接受返回值,进而将真正的返回值隐藏在该对象中。