In the real world there are many systems where collisions are not very frequent. For example if 2 users are processing bank transfers they may work with account objects but the accounts are different, so that they do not collide.
For the situation described above, optimistic locking will be a reasonable solution. When optimistic locking is used, it is accepted that collisions may occur, but instead of trying to prevent them, the system tries to detect and resolve them. Optimistic locking process flow is shown on the figure below.There are different ways to detect a collision. For example, you can mark the retrieved objects with a unique identifier, timestamp or username. Alternatively, you can store a copy of the originally retrieved object and compare it to the object from the database before trying to update.