The Servlet Specification JSR-315 clearly defines the web container behavior in the
service (and doGet, doPost, doPut etc.) methods (18.104.22.168 Multithreading Issues, Page 9):
"A servlet container may send concurrent requests through the service method of the servlet. To handle the requests, the Servlet Developer must make adequate provisions for concurrent processing with multiple threads in the service method.
Although it is not recommended, an alternative for the Developer is to implement the
SingleThreadModelinterface which requires the container to guarantee that there is only one request thread at a time in the service method. A servlet container may satisfy this requirement by serializing requests on a servlet, or by maintaining a pool of servlet instances. If the servlet is part of a Web application that has been marked as distributable, the container may maintain a pool of servlet instances in each JVM that the application is distributed across.
For servlets not implementing the
SingleThreadModelinterface, if the service method (or methods such as doGet or doPost which are dispatched to the service method of the HttpServlet abstract class) has been defined with the synchronized keyword, the servlet container cannot use the instance pool approach, but must serialize requests through it. It is strongly recommended that Developers not synchronize the service method (or methods dispatched to it) in these circumstances because of detrimental effects on performance".
Servlets are not thread safe and you have to synchronize your code manually what usually leads to esoteric implementations.
On demand workshops: Java EE 7: Bootstrap, Effective, Testing and Microservices available for streaming.