Main idea: Fields should be immutable by default. As long as data is immutable, threading is no problem since there is no writer modifying any data.
Usage of streams and recursion avoids the need for modifiable variables.
Analysis to ensure modified fields are local
Any fields that are modified and hence mutable need to be analyzed to find if they can escape the current thread of execution. As long as they do not escape the current call context, they are accessed only by one single thread and hence they are thread safe.
Problematic are mutable fields that are accessible by several threads. These should by default be forbidden.
Such non-local mutable fields require an explicit lock that must be held for every access to such a field. Either a run-time check or --better-- static analysis has to ensure that the lock is held for every feature that accesses such a field. Locks should allow two kinds of access: read or read/write. Read can be allowed for several threads, only read/write must be exclusive.
For development and debugging purposes, mutable fields can alternatively be marked as racy. This will flag a compile time warning and should not be allowed in production code, but should allow a quick solution during development when occasional errors are acceptable, e.g., for performance counters, logging, etc.
Alternatively, all features that contain mutable fields could be made thread local. This would have the effect that each thread lives in its own parallel universe and sees its own version of all modifiable state. However, there would be no communication between these parallel universes.
Features that contain mutable fields could be shared between threads if accesses are protected by a lock. Such protected shared features would create worm-holes for communication between threads. Locks could be multiple-reader single-writer locks.