Chatted with Michael Braun about this a bit...
I think computeIfAbsent is close to what we want, but it doesn't let us easily preserve the current behaviour to throw IOException - we'd have to wrap it as an RTE. I'm also a little sceptical of the claim that multiple threads would be attempting to calculate the same fingerprint, since this is only called when we open a new searcher.
I'll agree that it may be expensive, but I don't what code paths we are trying to protect against. And I think we'd get much better gains by doing the optimizations at the lower level... There's a note in IndexFingerprint that // TODO: this could be parallelized, or even cached per-segment if performance becomes an issue, which I think is where we should look instead of trying to ensure that a fingerprint is only calculated once.
If we do need to ensure that each fingerprint is only calculated once for performance issues, then we should switch to a more granular locking mechanism. Possibly something like striped locking, or a parallel Map<Long, Lock>. This is a lot more complexity.