private void createBuffers(int threadCount, ByteBufferAllocator alloc) throws IOException {
    ExecutorService pool = Executors.newFixedThreadPool(threadCount);
    int perThreadCount = bufferCount / threadCount;
    int reminder = bufferCount % threadCount;
    try {
      List<Future<ByteBuffer[]>> futures = new ArrayList<>(threadCount);
      // Dispatch the creation task to each thread.
      for (int i = 0; i < threadCount; i++) {
        final int chunkSize = perThreadCount + ((i == threadCount - 1) ? reminder : 0);
        futures.add(pool.submit(() -> {
          ByteBuffer[] chunk = new ByteBuffer[chunkSize];
          for (int k = 0; k < chunkSize; k++) {
            chunk[k] = alloc.allocate(bufferSize);
          }
          return chunk;
        }));
      }
      // Append the buffers created by each thread.
      int bufferIndex = 0;
      try {
        for (Future<ByteBuffer[]> f : futures) {
          for (ByteBuffer b : f.get()) {
            this.buffers[bufferIndex++] = b;
          }
        }
        assert bufferIndex == bufferCount;
      } catch (Exception e) {
        LOG.error("Buffer creation interrupted", e);
        throw new IOException(e);
      }
    } finally {
      pool.shutdownNow();
    }
  }
We allocate a number of byte buffers as the capacity.
@param capacity  total size of the byte buffer array
@param allocator the ByteBufferAllocator that will create the buffers
@throws IOException throws IOException if there is an exception thrown by the allocator
hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferArray.java