Consider the following method:
public void upsert(int customerId, int somethingElse) {
// some code which is prone to race conditions
}
The concept you're looking for is called segmented locking or striped locking. It is too wasteful to have a separate lock for each customer (locks are quite heavyweight). Instead you want to partition your customer ID space into a reasonable number of partitions, matching the desired degree of parallelism. Typically 8-16 would be enough, but this depends on the amount of work the method does.
This outlines a simple approach:
private final Object[] locks = new Object[8];
synchronized (locks[customerId % locks.length]) {
...implementation...
}