The future is used to get the results of an asynchronous operation, while promise is more abstract and cannot directly guess its function.
Future
The future comes first from the JDK's java.util.concurrent.Future, which is used to represent the result of an asynchronous operation.
The result of the operation can be obtained through the Get method, and if the operation is not completed, the thread that is currently called is synchronized, and if blocking is not allowed for too long or indefinitely, the result can be obtained through a GET method with a time-out, or if the time-out operation is still not completed, The timeoutexception is thrown. The Isdone () method can be used to determine whether the current asynchronous operation is complete and, if completed, returns true regardless of success or false. Cancel can attempt to cancel an asynchronous operation, the result of which is unknown, if the operation has already completed, or if another unknown reason has been rejected, the cancellation will fail.
Since the future of Netty is related to asynchronous I/O operations, it is named Channelfuture, which is related to channel operations.
In Netty, all I/O operations are asynchronous, which means that any I/O calls are returned immediately, rather than waiting for the operation to complete like traditional bio. An asynchronous operation poses a problem: How does the caller get the results of an asynchronous operation? Channelfuture is specifically designed to solve this problem.
There are two states of Channelfuture: uncompleted and completed. When an I/O operation is started, a new channelfuture is created, at which point it is in the uncompleted state-non-failed, non-successful, non-canceled because the I/O operation is not complete at this time. Once I/O is complete, the channelfuture will be set to completed, which has the following three possible results.
- Operation success;
- Operation failed;
- The operation was canceled.
Channelfuture provides a new set of APIs for obtaining operation results, adding event listeners, canceling I/O operations, synchronizing waits, and more.
Netty strongly recommends that you get the results of I/O operations directly by adding listeners, or follow-up related actions.
Channelfuture can add one or more genericfuturelistener at the same time, or remove Genericfuturelistener from the Remove method.
When the I/O operation is complete, the I/O thread calls back the Operationcomplete method of Genericfuturelistener in Channelfuture and treats the Channelfuture object as the method's entry parameter. If the user needs to do context-sensitive operations, the context information needs to be saved to the corresponding channelfuture.
The reason for the recommendation to replace Channelfuture's Get by Genericfuturelistener is that when we do asynchronous I/O operations, the time to complete is unpredictable, and if the timeout is not set, it causes the calling thread to be blocked for a long time, or even dead. and set the timeout time, the time can not be accurately predicted. Using the asynchronous notification mechanism callback Genericfuturelistener is the best solution and its performance is optimal.
PS: Do not call Channelfuture's await () method in Channelhandler, which results in deadlocks. The reason is that after initiating an I/O operation, the I/O thread is responsible for asynchronously notifying the user thread initiating the I/O operation, if the I/O thread and the user thread are the same thread, causing the I/O thread to wait for its own notification to complete, which leads to a deadlock, which is different from the classic two threads waiting for deadlocks belonged to himself to hang himself dead.
There are two types of timeouts for asynchronous I/O operations: One is the I/O timeout at the TCP level and the other is the operation timeout at the business logic level. There is no necessary connection between the two, but typically the business logic time-out should be greater than the I/O timeout, both of which are the included relationships.
The Ps:channelfuture timeout does not represent an I/O timeout, which means that if the connection resource is not closed after the Channelfuture timeout, then the connection may still succeed, which can cause serious problems. Therefore, it is often necessary to consider whether to set the I/O timeout or the channelfuture timeout.
Channelfuture Source Code Analysis
The abstractfuture implements the future interface, which does not allow I/O operations to be canceled.
@Override PublicV get ()throwsinterruptedexception, executionexception {//The await () method is called to block indefinitely and is notify () when the I/O operation is complete. await (); Throwable cause=cause (); //The program continues down to check if the I/O operation has an exception if(Cause = =NULL) { //If there is no exception, the result is obtained through the Getnow () method and returned. returnGetnow (); } //otherwise, the exception stack is wrapped and thrown executionexception. Throw Newexecutionexception (cause); } @Override PublicV Get (LongTimeout, timeunit unit)throwsinterruptedexception, Executionexception, timeoutexception {//call the await (long timeout, Timeunit unit) method to if(await (timeout, unit)) {Throwable cause=cause (); //If there is no time-out, then determine if an I/O exception occurred, etc. if(Cause = =NULL) { //The operation is the same as the Get method without parameters. returnGetnow (); } Throw Newexecutionexception (cause); } //if it times out, TimeoutException is thrown. Throw Newtimeoutexception (); }
Promise
Promise is a writable future,future itself and does not have a write-related interface, Netty extends the future through promise for setting the results of I/O operations.
Promise related write-operation interface definitions:
When Netty initiates an I/O operation, a new Promise object is created, such as when the Write (object) method of Channelhandlercontext is called, a new channelpromise is created.
@Override Publicchannelfuture Write (Object msg) {returnWrite (msg, newpromise ()); } @Override Publicchannelfuture Write (Object msg, Channelpromise Promise) {Defaultchannelhandlercontext next=Findcontextoutbound (Mask_write); Next.invoker.invokeWrite (Next, MSG, promise); returnpromise; } @Override Publicchannelpromise newpromise () {return NewDefaultchannelpromise (channel (), Executor ()); }
Promise Source Code Analysis
Analyze a source code for its implementation subclass Defaultpromise.
Implementation of Setsuccess method
@Override PublicPromise<v>setsuccess (V result) {//The SetSuccess0 method is called and the result of its operation is judged, and if the operation succeeds, the Notifylisteners method is called to notify listener. if(SETSUCCESS0 (Result)) {notifylisteners (); return This; } Throw NewIllegalStateException ("Complete already:" + This); } Private BooleansetSuccess0 (V result) {//The first is to determine whether the current promise operation results have been set, if it has been set, it is not allowed to repeat settings, the return of the settings failed. if(IsDone ()) {return false; } //because of the possible presence of I/O threads and user threads operating promise simultaneously, it is necessary to set the result of the operation with lock protection to prevent concurrent operation. synchronized( This) { //The return operation fails if the result of the operation is set two times (in order to improve the concurrency performance of two judgments), if it has already been set. if(IsDone ()) {return false; } //Judging the result of the operation, if it is empty, it simply requires that the notify is waiting for the business thread and does not contain the specific business logic object. //Therefore, the result is set to the system default success. if(Result = =NULL) { This. result =SUCCESS; } Else { //If the result of the operation is not NULL, the result is set to result. This. result =result; } //If there are user threads or other system threads that are waiting for the asynchronous I/O operation to complete if(Haswaiters ()) {//call the Notifyall method to wake up all the threads that are waiting. Note that both the Notifyall and wait methods must be used within the synchronization block. Notifyall (); } } return true; }
Implementation of the Await method
@Override PublicPromise<v> await ()throwsinterruptedexception {//If the current promise is already set, it is returned directly. if(IsDone ()) {return This; } //throws an interrupt exception if the thread has been interrupted. if(thread.interrupted ()) {Throw Newinterruptedexception (toString ()); } //Lock the current promise object with the Sync keyword synchronized( This) { //using cyclic judgment to judge the isdone results, the reason for the cyclic judgment is to prevent the thread from being accidentally woken up due to a function exception. while(!IsDone ()) {Checkdeadlock (); Incwaiters (); Try{wait (); } finally{decwaiters (); } } } return This; }
Because calling promise's await in an I/O thread or the sync method causes a deadlock, a protective check of the deadlock is required in the loop body to prevent the I/O thread from hanging dead, and the Java.lang.Object.wait () method is called to wait indefinitely until the i/ The o thread calls the Setsuccess method, the Trysuccess method, the Setfailure, or the Tryfailure method.
Future and promise