During yesterday's excellent talk of Heinz Kabutz at Jazoon, it turned out, that even the += operator isn't atomic and can cause problems in parallel, multicore, multiprocessor platforms. It doesn't have to be super-parallel - just current commodity hardware can already cause the problems - and you have to synchronized your code (without synchronized :-)).
Nevertheless, because EJB (1.0, 1.1, 2.0, 2.1, 3.0 and probably 3.1) are always executed in a dedicated thread - they appear as single threaded for the developer. For every thread (=user request, transaction) a new EJB instance is created (or reused from pool) - there is actually no shared state, except you access singletons, files etc. - which is not allowed. Actually the spec even prohibits the usage of the synchronization primitives and threading functionality inside EJBs.
Furthermore the programming model is rather procedural (or if you will "service oriented") - so in the @Stateless Session Beans the methods process some input data and give it back to the caller. Even the @Stateful Session Beans do not brake the rule - the container still prohibits concurrent access to the @Stateful instance. Because a @Stateful Session Bean can be only executed in a single thread - you do not have (you shouldn't!) care about the threading either.
The best of all: if you acess the JPA-persistence - the container synchronizes the data for you. Every transaction receives a copy of the JPA-entity (it is often implemented in this way), so nothing bad can happen even in this case.
I'm not sure whether there are actually some corner cases - where EJB programming model breaks in multicore environment (except you are violating the spec). Every EJB-instance can be only executed in a single thread. And every thread is (hopefully) executed in a single core. So if you keep the transactions short, and do not "hack" the EJBs accessing static variables or singletons - you are on the bright side :-).
NEW: Online Workhop Effective WebApps without Frameworks is also coming to: MUC Airport.