JAVA实现async/await异步处理等待机制
搜遍了百度都没看到JAVA有实现async/await的方案,而js 、.net 、scala都有async/await,心里感觉特别不爽,为什么JAVA没有呢,实现async/await很难吗? 于是参考了scala的实现方式,感觉看到了一点希望,觉得java也是可以像scala那样实现的,于是开始了以下尝试:
实现异步等待Async类:
/**
 * @author ffychina
 * @since 2020-04-13
 * https://blog.csdn.net/ffychina
 * */
 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class Async{
	private List<AsyncFunction> asyncFnList=new ArrayList<Async.AsyncFunction>();
	public Async(){
	}
	public Async(AsyncFunction runable){
		add(runable);
	}
	public Async add(AsyncFunction runable){
		asyncFnList.add(runable);
		return this;
	}
	public Object await(){
		CountDownLatch latch=new CountDownLatch(asyncFnList.size());
		for(AsyncFunction asyncFn:asyncFnList){
			asyncFn._latch[0]=latch;
			Thread thread=new Thread(asyncFn);
			thread.start();
		}
		try{
			latch.await();
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		List result=this.asyncFnList.get(0)._result;
		if(asyncFnList.size()==0){
			return result.get(0);
		}else{
			return result.toArray();
		}
	}
	@FunctionalInterface
	public static interface AsyncFunction extends Runnable{
		Object call();
		List _result=new ArrayList();
		CountDownLatch[] _latch=new CountDownLatch[1];
		@Override
		default void run(){
			synchronized(_result){
				_result.add(call());
			}
			_latch[0].countDown();
		}
		default Object result(){
			return _result.toArray(new Object[0]);
		}
	}
}
异步线程测试用例:
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import org.nutz.http.Http;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
TimeInterval timer=DateUtil.timer();
Object result=new Async(()->{
	int courseId=Double.valueOf(Math.random()*100000).intValue();
	String url="http://127.0.0.1:8080/hello/"+courseId;
	String res=Http.get(url).getContent();
	log.info(res);
	return res;
}).add(()->{ //增加多一个异步线程
	String url="http://127.0.0.1:8080/hello/world";
	String res=Http.get(url).getContent();
	log.info(res);
	return res;
}).await();
log.info(StrUtil.join(",",result));
log.info("finish test,spend ms: "+timer.interval());
异步线程处理结果:
20-04-13 09:42:57.194 INFO [Thread-0] {"method":"post","param1":"hello","param2":"16686"}
20-04-13 09:42:57.196 INFO [Thread-1] {"method":"post","param1":"hello","param2":"world"}
20-04-13 09:42:57.206 INFO [main] {"method":"post","param1":"hello","param2":"16686"},{"method":"post","param1":"hello","param2":"world"}
20-04-13 09:42:57.207 INFO [main] finish test,spend ms: 452
分享心得:
以上代码实现只是为了抛砖引玉,希望大家都给点建议或加以完善。我的出发点是在使用vertx框架时能更好的利用异步处理,写出更容易阅读和理解的代码风格。
另外,如何更好地结合Promise目前还没有想好该怎么做,也希望能听一下大家的建议。
https://blog.csdn.net/ffychina/article/details/105482052
https://blog.csdn.net/weixin_34117211/article/details/92065633
异步编程的最高境界,就是根本不用关心它是不是异步
.NET的async/await方式最先达到了这个境界。
和async/await写法相比,Java的什么ExecutorService以及回调之类设计都是惭愧无比的。当然,习惯了也没啥,反正那Java的lamda表达式用着也可以接受。
人不知道更好的东西的时候其实也就无所谓难受,更何况现在Java强势,.NET就算有好东西也发不出太大的影响...
不过可以想象,如果Java9支持了这个写法,那么那些RxJava什么的和回调有关系的各种lib都会成为二等公民(对于某数据流式处理还是需要的)。
其实那么多牛人,怎么会想不到这点呢,为啥还不改呢?也许是不想承受抄袭的骂名,而是想改得直接超越.NET的,也许是专利限制,谁知道呢。
想来又不是要改JVM,只是改点编译器罢,支持把await的后的代码自动编程回调函数,再把系统的一些费时间的method给多封装一个返回AsyncResult<SomeReturnType>之类的method,多大的事儿啊,改了吧。
那些getter/setter不支持的事儿就不和你计较了,反正还有个Lombk预编译加上@Gettr/@Setter之类的就可以了。可这个async/await不然,就不容易由第三方用类似的方法实现了。
顺便普及一下async+await是如何优雅的,最自然的语法,天才的想法。思路大致是这样:
- 假设先做个很重的API,为了异步,那就立刻返回一个AsyncResult<真实返回类型>,就是不是立刻得到结果的那种,同时在单独的线程池里执行本来的动作。 一般的编程模式是,让用户通过回调来处理结果:
 
- 
string someFunction() {
 - 
AsyncResult<真实返回值类型> asyncResult = someApi(...);
 - 
asyncResult.onComplete( 真实结果 -> {
 - 
...对结果的处理...
 - 
} );
 - 
return something;
 - 
}
 
- 就在这时,有哪位聪明人,觉得可以让编译器作者事儿,只要代码里加个标记就行了,这就是await的登场了
 
- 
string someFunction() {
 - 
真实返回值类型> result = await someApi(...);
 - 
...对结果的处理...
 - 
return something;
 - 
}
 
相当于编译器自动把await语句后面的知道函数末尾的所有语句都编译成了一个临时的Lamda表达式了。 3. 但是这样一来,异步的特性会逐层传染,当前所在的函数someFunction也就立刻返回了,那到底返回成什么好呢,这就是async的登场了。
- 
async string someFunction() {
 - 
真实返回值类型> result = await someApi(...);
 - 
...对结果的处理...
 - 
return something;
 - 
}
 
这就是告诉编译器,谁要是调用someFunction,他也必须用await罩一下。
- 
string x = await someFunction()
 - 
...
 
就这么想着想着改巴改巴就成了。
更具体的,就随手转帖一个有关.NET async/await的实现背后 « 司维的思维。
async/await多美啊,实在符合自然思维,看到await,就明白后面的代码会等会儿再执行,暂时可以回家吃饭去了。
但是,也有缺点,await只能取得一个结果,然后就不搞了。 这对于陆陆续续出若干个结果的API,就没什么作用,难道把一个流作为返回值,那最终,还是需要RxJava那种针对流处理的Publisher/Subscriber+Callback的方式。 这就是yield的登场了,它没有async/await直观好懂,就不多说了。
顺便转个 JavaScript async/await 函数的含义和用法 - 阮一峰的网络日志,里面也有提到yield,看看就知道了。
真想不到以前弱弱的Java和JavaScript成长到现在这么强大,真真是凝聚着无数高手们的心血,硬是挤得.NET直哆嗦(即使.NET在实现上依然优于Java,但是事儿不是那么单纯的)。真是白让我以前那么喜欢.NET。
https://zhuanlan.zhihu.com/p/180473802
虽然Java官方有loom项目来实现协程,但是实在等不住了。既然fanx支持async/await,所以就尝试和异步IO结合,来实现高性能网络框架。
代码见这里:fanx-dev/asyncServer。
架构类似于netty的reactor模式,像这样:

示例代码:
class TestServer : Handler {
  override async Void onService(Socket socket) {
    buf := NioBuf.makeMem(1024)
    n := await socket.read(buf, 1)
    buf.flip()
    echo("=====server receive: $buf: "+buf.readAllStr)
    buf.clear
    buf.printLine("HelloWorld")
    buf.flip
    n2 := await socket.write(buf)
    echo("=====send: "+n2)
  }
  static Void main() {
    Server {
      port = 8080
      handler = TestServer#
    }.start
  }
}
代码中,当运行到await的时候当前任务暂停,并交给selector线程去监听IO事件,当前线程没有阻塞而是去继续处理其他连接任务。当selector发现事件就绪后通知恢复运行任务。由于采用线程池机制await前后可能运行在不同的线程中。
是不是代码看起来比netty的回调好多了。
支持基本的Http协议:
class HttpTestServer : HttpHandler {
  override async Void onHttpService(HttpReq req, HttpRes res) {
    echo("Server receive: "+req.headers)
    res.headers["Content-Type"] = "text/html; charset=utf-8"
    buf := NioBuf.makeMem(1024)
    buf.printLine("<html>
                        <body>Hello World</body>
                       </html>")
    buf.flip
    //await res.writeFixed(buf)
    await res.writeChunk(buf)
  }
  static Void main() {
    Server {
      port = 8080
      handler = HttpTestServer#
    }.start
  }
}
性能方面没有测试,估计肯定比阻塞IO好。
Java基于协程的IO框架还可以使用kilim,本项目也参考了kilim的代码。
https://github.com/fanx-dev/asyncServer
https://vertx.io/
https://github.com/reactivex/rxjava
https://vertx.io/docs/vertx-rx/java/
文本处理
http://www.nltk.org/
https://textblob.readthedocs.io/en/dev/
https://spacy.io/
https://pypi.org/project/gensim/
https://stanfordnlp.github.io/CoreNLP/
http://tonetsutomu.com/tone/
https://raft.github.io/
http://blog.datadive.net/selecting-good-features-part-ii-linear-models-and-regularization/
https://scikit-learn.org/stable/tutorial/index.html
http://archive.ics.uci.edu/ml/index.php
http://ipython.org/ipython-doc/1/interactive/public_server.html
www.kaggle.com/
https://www.coursera.org/
https://www.coursera.org/learn/machine-learning
https://www.coursera.org/learn/probabilistic-graphical-models
https://cloud.google.com/dataproc
https://iansommerville.com/software-engineering-book/
https://www.bilibili.com/video/av61253978/
                    
                
                
            
        
浙公网安备 33010602011771号