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