I know that it's possible to make the GeoIPLite legacy Java API thread-safe by using certain caching parameters, but the GeoIP2 docs for Java or C don't mention the word "thread" at all.
I'd like to be able to build() a DatabaseReader once and then do city() on it in several different threads. Is this safe?
Yes, the GeoIP2 Java API is thread safe. It is recommended to open one reader and share that between threads. This is documented in the README.md.