public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    Objects.requireNonNull(type, "type must not be null");
    TypeAdapter<?> cached = typeTokenCache.get(type);
    if (cached != null) {
      @SuppressWarnings("unchecked")
      TypeAdapter<T> adapter = (TypeAdapter<T>) cached;
      return adapter;
    }

    Map<TypeToken<?>, TypeAdapter<?>> threadCalls = threadLocalAdapterResults.get();
    boolean isInitialAdapterRequest = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<>();
      threadLocalAdapterResults.set(threadCalls);
      isInitialAdapterRequest = true;
    } else {
      // the key and value type parameters always agree
      @SuppressWarnings("unchecked")
      TypeAdapter<T> ongoingCall = (TypeAdapter<T>) threadCalls.get(type);
      if (ongoingCall != null) {
        return ongoingCall;
      }
    }

    TypeAdapter<T> candidate = null;
    try {
      FutureTypeAdapter<T> call = new FutureTypeAdapter<>();
      threadCalls.put(type, call);

      for (TypeAdapterFactory factory : factories) {
        candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          // Replace future adapter with actual adapter
          threadCalls.put(type, candidate);
          break;
        }
      }
    } finally {
      if (isInitialAdapterRequest) {
        threadLocalAdapterResults.remove();
      }
    }

    if (candidate == null) {
      throw new IllegalArgumentException(
          "GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
    }

    if (isInitialAdapterRequest) {
      /*
       * Publish resolved adapters to all threads
       * Can only do this for the initial request because cyclic dependency TypeA -> TypeB -> TypeA
       * would otherwise publish adapter for TypeB which uses not yet resolved adapter for TypeA
       * See https://github.com/google/gson/issues/625
       */
      typeTokenCache.putAll(threadCalls);
    }
    return candidate;
  }
@Test
  public void testGetAdapter_Concurrency() {
    class DummyAdapter<T> extends TypeAdapter<T> {
      @Override
      public void write(JsonWriter out, T value) throws IOException {
        throw new AssertionError("not needed for this test");
      }

      @Override
      public T read(JsonReader in) throws IOException {
        throw new AssertionError("not needed for this test");
      }
    }

    final AtomicInteger adapterInstancesCreated = new AtomicInteger(0);
    final AtomicReference<TypeAdapter<?>> threadAdapter = new AtomicReference<>();
    final Class<?> requestedType = Number.class;

    Gson gson =
        new GsonBuilder()
            .registerTypeAdapterFactory(
                new TypeAdapterFactory() {
                  private volatile boolean isFirstCall = true;

                  @Override
                  public <T> TypeAdapter<T> create(final Gson gson, TypeToken<T> type) {
                    if (isFirstCall) {
                      isFirstCall = false;

                      // Create a separate thread which requests an adapter for the same type
                      // This will cause this factory to return a different adapter instance than
                      // the one it is currently creating
                      Thread thread =
                          new Thread() {
                            @Override
                            public void run() {
                              threadAdapter.set(gson.getAdapter(requestedType));
                            }
                          };
                      thread.start();
                      try {
                        thread.join();
                      } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                      }
                    }

                    // Create a new dummy adapter instance
                    adapterInstancesCreated.incrementAndGet();
                    return new DummyAdapter<>();
                  }
                })
            .create();

    TypeAdapter<?> adapter = gson.getAdapter(requestedType);
    assertThat(adapterInstancesCreated.get()).isEqualTo(2);
    assertThat(adapter).isInstanceOf(DummyAdapter.class);
    assertThat(threadAdapter.get()).isInstanceOf(DummyAdapter.class);
  }