Saying gRPC server is multi-threaded is a vague statement. At the transport level all messages from a stream are handled by the same transport thread. When dispatching deframed messages from a stream to the application, gRPC dispatches messages to the StreamObserver implemented by the application via the call executor (that can be supplied to the Grpc server builder). It is here that different messages could be handled by different threads in the call executor. The code supplied to the call executor however is a wrapper code that calls the StreamObserver in a
serialized manner by serializing them in the call's
Context. This makes sure that different messages from the same stream are still handled in the received order.
Note: The above answer elucidates using Java but the idea will be similar in other languages.