This is mostly to manage timing issues with changing connection state and sending notifications. This is a little wacky, but the specific case we're protecting against is:

1. Caller starts a connection attempt via `connect()`.
2. Caller asks connection to shut down.
3. `shutdownImmediately()` checks if we've completed the connection process, but we haven't yet, so it starts preparing a shutdown `Runnable`.
4. While that preparation is happening, the handshake completes, the connection gets added to the active connection pool in the `PushManager` and sends one or more messages.
5. The shutdown `Runnable` gets added to the connection's `eventLoop`, but after a notification has been sent.
6. We lose track of the notification because we did a non-graceful shutdown after the notification was sent.

Synchronizing everything guarantees that we can't try to send a notification if we're in the middle of preparing the shutdown `Runnable`. Because the connection's `eventLoop` is bound to a single thread, it's guaranteed that if we add the shutdown `Runnable` before making a write attempt, the `Runnable` will go first and the write will fail, so we'll still have everything in a known state.

Very open to other ideas for how to do this.