Assume that you write a servlet and synchronize the service(...) (or any of the doXXX(...) methods or any method you call from any of these methods). Once you deploy the servlet, the servlet container receives ten "near-simultaneous" requests for the servlet. Upon receipt of the first request, the servlet container kicks off a thread and sends it on its way (i.e. starts it executing the service(...) method).
Unbeknownst to the servlet container, the thread executing in the service(...) method "locks the door" behind it (holds the lock for the servlet object). Not knowing any better, the servlet container starts the other nine threads and assumes they are happily executing the service(...) method. In reality, each of the nine other threads finds the object (the servlet instance) locked, so they go into a "wait" state.
Meanwhile, the first thread finishes executing the service(...) method, so it releases the lock on the servlet instance. One of the other threads--not necessarily the second thread, so we'll say the eighth thread--awakens to find the servlet's lock available, so it requests the lock and starts processing the service(...) method. Meanwhile, the servlet container may be receiving other requests. It's possible that when the eighth thread (which was the second to execute the service(...) method) finishes, the thirty-fifth thread grabs the lock.
With this approach each thread has an "equal" probability of being the next allowed to execute the service(...) method. It's complete anarchy--every thread for itself. About this time, the person who sent the second request is calling you to say that your application is down. :) And, if you really want things to get complex, try synchronizing the service(...) method and the doXXX(...) methods...
So what does the servlet container do? It provides queueing and prioritization (generally first-in-first-out) of requests.
Some servlet containers instantiate multiple servlet objects (and maintain a servlet pool--remember that synchronizing a method locks the object (or instance), not the method, so multiple threads can execute the same method in different instances) to handle multiple simultaneous requests. (Note that this means that you may need to synchronize access of static members.)
Others (like Tomcat, IIRC) provide as little support for SingleThreadModel servlets as is required by the servlet specification (they don't waste time creating object pools) since using SingleThreadModel is a "cop out." Keep reading, I'll explain why... :) Implementing the interface often gives developers a false sense of security when it comes to multi-threaded programming. For example, regardless of whether your servlet implements SingleThreadModel or not, you still need to consider synchronization problems with static members, HttpSession attributes, ServletContext attributes, and any other classes outside the scope of the servlet (i.e. JavaBeans, support classes, etc.).
So, what do you do if you're not sure whether your servlet is thread-safe? Your best bet is to talk with other developers who have more experience in multi-threaded programming (someone who's been doing it for 30 years is a good beginner ;). Also, do all you can to learn about the topic. IBM's DeveloperWorks website has a lot of articles on multi-threaded programming, including Understand that for instance methods, synchronized locks obj..., Threading lightly, Part 1: Synchronization is not the enemy(and parts 2 and 3), and many, many more. Also, check out some of their tutorials, like Introduction to Java threads.