Open itboy2009 opened 4 years ago
@itboy2009
The point of the project is to make it look very easy to write non-blocking code.
The project includes a class transformer that rewrites the bytecode of your methods so that they don't actually block any threads. When the execution of the method hits a call to await()
on CompletableFuture
that isn't done
it wires up a callback on whenComplete
and returns from the entire method immediately. When that callback is invoked it resumes within the middle of the method on a different thread by using a state machine.
@HaloFour
First of all, thank you for sharing However, the test result is not what I expected The test results show that: await() will block the thread
here is my code:
`import com.ea.async.Async;
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;
import static com.ea.async.Async.await;
public class DemoCases { /**
If the await() method does not block, then the thread can be reused */ private static final ExecutorService workThreadPool = Executors.newFixedThreadPool(1);
/**
This simulates a blocking io operation */ private int blockIO() throws Exception { HttpURLConnection httpURLConnection = (HttpURLConnection) new URL("https://github.com/electronicarts/ea-async/issues/54").openConnection(); httpURLConnection.connect(); InputStream in = httpURLConnection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder responseStringBuffer = new StringBuilder(); String line; while( (line = reader.readLine()) != null ){ responseStringBuffer.append(line); } int contentLength = responseStringBuffer.length(); return contentLength; }
public CompletableFuture
public void otherMethodUseWorkThreadPool() { workThreadPool.submit(() -> { for(int i=0;i<5;i++) { try { TimeUnit.MILLISECONDS.sleep(200); System.out.println(String.format("threadId: %s", Thread.currentThread().getId())); } catch (Exception e) { e.printStackTrace(); } } }); }
public void test() throws InterruptedException { ExecutorService dispatchThreadPool = Executors.newFixedThreadPool(2);
dispatchThreadPool.submit(() -> {
String result = await(blockSomeThingFuture()); // block thread
System.out.println(result);
});
TimeUnit.MILLISECONDS.sleep(50);
dispatchThreadPool.submit(() -> {
otherMethodUseWorkThreadPool(); // if await() don't block any threads. This code should be executed first
});
dispatchThreadPool.awaitTermination(100, TimeUnit.SECONDS);
}
public static void main(String args[]) throws InterruptedException { Async.init(); DemoCases demoCases = new DemoCases(); demoCases.test(); } }`
@itboy2009
The reason might be that the class transformer can't rewrite the class that's already executing. I'd suggest using the Java agent or the Maven plugin instead of relying on Async.init()
. There are instruction on the README of this repository.
@HaloFour
I have used agent, but the result is the same
I don't know where the problem is, You used it earlier, can you show your code?
I have found this library very useful, since it enables much simpler non blocking Rest API code than using a callback based model. Many other languages have this syntax, but EA Async is the only tool that enables it for Java. It is quite sad that this is not part of the language itself:
As an example, try to write the code in this class using a callback based syntax.
My blog has an advanced Java Spring Boot API that uses EA Async, and equivalent APIs in C# / NodeJS that implement the same requirements. For further Java coding details, see this blog post of mine on the Java non blocking coding model.
@itboy2009 can you try this?
public CompletableFuture<String> blockSomeThingFuture()
{
System.out.println("blockSomeThingFuture(): enter");
CompletableFuture<String> ret = CompletableFuture.supplyAsync(() -> {
for(int i=0;i<5;i++) {
try
{
System.out.println("blockSomeThingFuture(): before blockIO() call");
int contentLength = blockIO();
System.out.println(String.format("contentLength: %s, threadId: %s", contentLength, Thread.currentThread().getId()));
}
catch (Exception e)
{
e.printStackTrace();
}
}
return "done";
}, workThreadPool);
System.out.println("blockSomeThingFuture(): exit");
return ret;
}
And see if "blockSomeThingFuture(): exit" is output before "blockSomeThingFuture(): before blockIO() call"? If it isn't, then blockSomeThingFuture
isn't returning to await()
call before blocking thread
If it helps anyone I've put together a very simple Non Blocking Java Async Await API to demonstrate usage:
Code should be simple when using an async await coding model:
@papercuptech
here is the output:
blockSomeThingFuture(): enter blockSomeThingFuture(): exit blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 done
but, I don’t use ea-async, I can get the same output.
I mean, during the IO blocking period, ea-async did not release the thread to do other things
If it helps anyone I've put together a very simple Non Blocking Java Async Await API to demonstrate usage:
Code should be simple when using an async await coding model:
- No spinning up child threads
- No callbacks / lambdas
- No blocking code anywhere due to libraries such as Async HTTP Client
@gary-archer
public static <T> T myAwait(CompletableFuture completableFuture) {
return (T) completableFuture.toCompletableFuture().join();
}
Use myAwait() instead of await(), you can get the same result
Is this the value of ea-async? I'm confused
@gary-archer
Are you using the maven or gradle plugins to transform the classes before attempting to decompile them?
I just tried the following simple project:
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import com.ea.async.Async;
public class Program {
public static void main(String[] args) {
Async.init();
Test test = new Test();
CompletableFuture<String> future = test.blockSomeThingFuture();
Test.log("awaiting future");
String result = Async.await(future);
Test.log("complete: " + result);
}
}
class Test {
private static final Timer timer = new Timer();
private static final Random random = new Random();
public CompletableFuture<String> blockSomeThingFuture()
{
log("blockSomeThingFuture(): enter");
try {
Async.await(simulateIo());
return CompletableFuture.completedFuture("Hello!");
} finally {
log("blockSomeThingFuture(): exit");
}
}
private CompletableFuture<Void> simulateIo() {
CompletableFuture<Void> future = new CompletableFuture<>();
long delay = random.nextInt(1000) + 1000;
timer.schedule(new TimerTask() {
@Override
public void run() {
future.complete(null);
}
}, delay);
return future;
}
public static void log(String message) {
Thread currentThread = Thread.currentThread();
System.out.printf("[%d:%s] %s%n", currentThread.getId(), currentThread.getName(), message);
}
}
The output was:
[1:main] blockSomeThingFuture(): enter
[1:main] awaiting future
[14:Timer-0] blockSomeThingFuture(): exit
[1:main] complete: Hello!
You can see from the output that Test#blockSomeThingFuture
returns immediately when it hits Async#await
and the CompletableFuture<Void>
is not yet done. Then the method resumes executing on the timer thread once the timer has elapsed and completed the future.
Thanks @HaloFour - I've used EA Async quite a bit in the past for sample purposes - the goals behind it are good - and when I've looked at decompiled code it has always looked correct.
A busy week at work this week so maybe I'm just confusing myself. Will look at your code at the weekend. Enjoying the discussion with you guys though ...
I haven't used ea-async in a while myself. My more recent projects are all based on Spring WebFlux and I never found a good synergy between the two nor do I feel like trying to adapt this project to work with Reactor.
One project you might want to keep your eyes on is Project Loom which looks to bring coroutine primitives and green threads to the Java runtime. Using them enables writing your own helper methods that behave like coroutines with parked green threads as the async state machine. I've emulated async
/await
using these primitives and it was pretty trivial and did not require an agent or byte code transformer. Although the main goal of the project is to make blocking I/O not such a bad thing by making it extremely cheap to block a green thread.
@HaloFour I release a new project JAsync implement async-await fashion in java which use Reactor as its low level framework. It is in the alpha stage. I need more suggest and test case. This project makes the developer's asynchronous programming experience as close as possible to the usual synchronous programming, including both coding and debugging.
When debugging, you can see all variables in the monitor window just like when debugging normal code.
await(blockSomeThingFuture());
just only equals to
this.blockSomeThingFuture().toCompletableFuture().join(); // block!!!
Why is it so complicated?
Or I didn’t understand, I look forward to your sharing