Code review comment: +1 to throwing `IllegalStateException` if the stream is closed. Otherwise we risk having regressions by changing the behavior. It's safer to throw the exception for the purposes of your change.

Code under review:
protected void onResponse(StreamingGetWorkResponseChunk chunk) {
      getWorkThrottleTimer.stop();
      long id = chunk.getStreamId();

      WorkItemBuffer buffer = buffers.computeIfAbsent(id, (Long l) -> new WorkItemBuffer());
      buffer.append(chunk);

      if (chunk.getRemainingBytesForWorkItem() == 0) {
        long size = buffer.bufferedSize();
        buffer.runAndReset();

        
        long numInflight = inflightMessages.decrementAndGet();
        long bytesInflight = inflightBytes.addAndGet(-size);

        
        
        
        if (numInflight < request.getMaxItems() / 2 || bytesInflight < request.getMaxBytes() / 2) {
          long moreItems = request.getMaxItems() - numInflight;
          long moreBytes = request.getMaxBytes() - bytesInflight;
          inflightMessages.getAndAdd(moreItems);
          inflightBytes.getAndAdd(moreBytes);
          final StreamingGetWorkRequest extension =
              StreamingGetWorkRequest.newBuilder()
                  .setRequestExtension(
                      StreamingGetWorkRequestExtension.newBuilder()
                          .setMaxItems(moreItems)
                          .setMaxBytes(moreBytes))
                  .build();
          executor()
              .execute(
                  () -> {
                    try {
                      synchronized (this) {
                        if (!clientClosed.get()) {
                          send(extension);
                        }
                      }
                    } catch (IllegalStateException e) {
                      
                    }
                  });
        }
      }
    }

Line commented on: if (!clientClosed.get()) {

Revised code:
protected void onResponse(StreamingGetWorkResponseChunk chunk) {
      getWorkThrottleTimer.stop();
      long id = chunk.getStreamId();

      WorkItemBuffer buffer = buffers.computeIfAbsent(id, (Long l) -> new WorkItemBuffer());
      buffer.append(chunk);

      if (chunk.getRemainingBytesForWorkItem() == 0) {
        long size = buffer.bufferedSize();
        buffer.runAndReset();

        
        long numInflight = inflightMessages.decrementAndGet();
        long bytesInflight = inflightBytes.addAndGet(-size);

        
        
        
        if (numInflight < request.getMaxItems() / 2 || bytesInflight < request.getMaxBytes() / 2) {
          long moreItems = request.getMaxItems() - numInflight;
          long moreBytes = request.getMaxBytes() - bytesInflight;
          inflightMessages.getAndAdd(moreItems);
          inflightBytes.getAndAdd(moreBytes);
          final StreamingGetWorkRequest extension =
              StreamingGetWorkRequest.newBuilder()
                  .setRequestExtension(
                      StreamingGetWorkRequestExtension.newBuilder()
                          .setMaxItems(moreItems)
                          .setMaxBytes(moreBytes))
                  .build();
          executor()
              .execute(
                  () -> {
                    try {
                      send(extension);
                    } catch (IllegalStateException e) {
                      
                    }
                  });
        }
      }
    }