Combine this with the actor model (using Akka or similar) gives you guaranteed "one message at a time" processing and you don't have to deal with locks.
So, you get to pick, "at most once" or "at least once." And then you need to build your system to act accordingly.
Low-ish volume- design your system such that data flows [at the relevant crucial points] through a single actor to ensure proper concurrency.
High volume- trickier but I think same idea in principle. First thought that comes to mind here is the new GenStage stuff in Elixir.
You may not have to do deal with locks at a local level. You absolutely have to deal with locks at a system level.