Unfortunately with the Servlet 3 async feature (based on blocking IO) there is no good way to detect when a client has gone away.
Suppose I set a ConcurrentMap on server side to wait for events and notify the client of changes. If the servlet code is unable to detect client disconnection, there is a risk that the map keeps some object longer than necessary
That's correct although the overhead of storing an AsyncContext is a lot smaller than using a thread. Also since the server timeout will typically be configured to one or two minutes, or under five anyway, every so often you can proactively set DeferredResult instances even if there are no new events and purge them from the ConcurrentMap.