I think you should use regular locks as well, because state transfer uses regular locks and you don't want to write the result of a remote get command as an L1 entry after that entry was already written to the data container as a regular entry. 

Actually, you probably need to check that the entry is not mapped to the local node and writing it to the data container while holding the shared StateTransferLock (a la DistributionLogic.commitEntry()). So your solution may still work during state transfer, but I haven't quite understood the L1ReadSynchronizer code yet so I'd prefer regular locks unless there's a big performance difference.

1. Regular locking achieves exactly the same result. And if you acquire the lock before sending the remote get command, there will also be only one thread sending a request for a key at any time.

2. The ability to cancel a pending remote get does sound like an interesting feature, as the invalidate command won't have to wait for the remote get to finish. But you would need to handle put commands originated from the local node as well, because they also write to L1. And the big question is how often will you have contention on the same L1 entry - I feel that if you have significant contention with L1 invalidations then you'd be better of disabling L1 entirely.

3a. I also thought that L1 invalidations used ZERO_LOCK_ACQUISITION_TIMEOUT, but I couldn't find anything like that so I assumed that's no longer the case. Anyway, I don't really see a reason why we have to use tryLock.

3b. That couldn't happen if you acquired the L1 lock before sending the remote get command.