Comprehensive Java library providing essential utilities, immutable collections, caching, and concurrency tools for modern Java development.
—
Enhanced concurrency utilities including listenable futures, rate limiting, improved executors, and synchronization primitives that extend Java's concurrent programming capabilities.
Enhanced Future that allows listeners to be attached for completion notification, enabling reactive programming patterns.
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
// Creating immediate futures
ListenableFuture<String> successful = Futures.immediateFuture("result");
ListenableFuture<String> failed = Futures.immediateFailedFuture(new RuntimeException("error"));
// Adding callbacks for completion notification
ListenableFuture<String> future = asyncOperation();
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("Operation succeeded: " + result);
}
@Override
public void onFailure(Throwable t) {
System.err.println("Operation failed: " + t.getMessage());
}
}, MoreExecutors.directExecutor());
// Chaining operations with transformation
ListenableFuture<String> input = getStringAsync();
ListenableFuture<Integer> length = Futures.transform(input,
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
}, executor);
// Asynchronous transformation (returns another ListenableFuture)
ListenableFuture<String> input2 = getKeyAsync();
ListenableFuture<String> result = Futures.transformAsync(input2,
new AsyncFunction<String, String>() {
@Override
public ListenableFuture<String> apply(String key) throws Exception {
return lookupValueAsync(key); // Returns ListenableFuture<String>
}
}, executor);Utilities for combining multiple futures into aggregate operations.
import com.google.common.collect.ImmutableList;
// Combine multiple futures into a list
ListenableFuture<String> future1 = getAsync("key1");
ListenableFuture<String> future2 = getAsync("key2");
ListenableFuture<String> future3 = getAsync("key3");
ListenableFuture<List<String>> combined = Futures.allAsList(future1, future2, future3);
// Result list contains values in same order as input futures
// Successful futures only (failures ignored)
ListenableFuture<List<String>> successful = Futures.successfulAsList(future1, future2, future3);
// Failed futures contribute null to result list
// Immediate values in list
List<ListenableFuture<String>> futures = ImmutableList.of(future1, future2, future3);
ListenableFuture<List<String>> fromList = Futures.allAsList(futures);
// Combining with custom logic
ListenableFuture<String> combined2 = Futures.whenAllSucceed(future1, future2, future3)
.call(new Callable<String>() {
@Override
public String call() throws Exception {
// All futures completed successfully
String result1 = Futures.getDone(future1);
String result2 = Futures.getDone(future2);
String result3 = Futures.getDone(future3);
return result1 + result2 + result3;
}
}, executor);
// First successful result
ListenableFuture<String> firstSuccessful = Futures.whenAllComplete(future1, future2, future3)
.call(new Callable<String>() {
@Override
public String call() throws Exception {
// Return first non-null result
for (ListenableFuture<String> future : Arrays.asList(future1, future2, future3)) {
try {
String result = Futures.getDone(future);
if (result != null) {
return result;
}
} catch (Exception ignored) {
// Continue to next future
}
}
throw new RuntimeException("All futures failed or returned null");
}
}, executor);Robust error handling and recovery patterns for asynchronous operations.
// Catching specific exceptions
ListenableFuture<String> risky = riskyOperation();
ListenableFuture<String> withFallback = Futures.catching(risky,
IOException.class,
new Function<IOException, String>() {
@Override
public String apply(IOException e) {
return "fallback-value"; // Recover from IOException
}
}, executor);
// Asynchronous exception handling
ListenableFuture<String> withAsyncFallback = Futures.catchingAsync(risky,
IOException.class,
new AsyncFunction<IOException, String>() {
@Override
public ListenableFuture<String> apply(IOException e) throws Exception {
return getFallbackValueAsync(); // Async recovery
}
}, executor);
// Multiple exception types
ListenableFuture<String> multiCatch = Futures.catching(
Futures.catching(risky, IOException.class, ioFallback, executor),
TimeoutException.class, timeoutFallback, executor);
// Transform exceptions
ListenableFuture<String> transformed = Futures.transform(risky,
new Function<String, String>() {
@Override
public String apply(String input) {
if (input == null) {
throw new IllegalStateException("Unexpected null result");
}
return input.toUpperCase();
}
}, executor);Adding timeouts and scheduling capabilities to futures.
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// Add timeout to future
ListenableFuture<String> slow = slowOperation();
ListenableFuture<String> withTimeout = Futures.withTimeout(slow, 30, TimeUnit.SECONDS, scheduler);
// Schedule async operation
ListenableFuture<String> scheduled = Futures.scheduleAsync(
new AsyncCallable<String>() {
@Override
public ListenableFuture<String> call() throws Exception {
return performScheduledTask();
}
}, 5, TimeUnit.SECONDS, scheduler);
// Submit callable to executor
ListeningExecutorService executor = MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(10));
ListenableFuture<String> submitted = Futures.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "computed result";
}
}, executor);ExecutorService that returns ListenableFuture instances instead of regular Futures.
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.Executors;
// Create ListeningExecutorService
ExecutorService threadPool = Executors.newFixedThreadPool(10);
ListeningExecutorService listeningExecutor = MoreExecutors.listeningDecorator(threadPool);
// Submit tasks that return ListenableFuture
ListenableFuture<String> future = listeningExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "task result";
}
});
ListenableFuture<Void> voidFuture = listeningExecutor.submit(new Runnable() {
@Override
public void run() {
performTask();
}
}, null);
// Direct executor (executes immediately on calling thread)
ListeningExecutorService directExecutor = MoreExecutors.newDirectExecutorService();
// Same-thread executor (for testing)
Executor sameThreadExecutor = MoreExecutors.directExecutor();
// Graceful shutdown utility
MoreExecutors.shutdownAndAwaitTermination(threadPool, 60, TimeUnit.SECONDS);Controls the rate at which operations can be performed, useful for throttling API calls or resource access.
import com.google.common.util.concurrent.RateLimiter;
// Create rate limiter (5 permits per second)
RateLimiter rateLimiter = RateLimiter.create(5.0);
// Acquire permits (blocking)
rateLimiter.acquire(); // Acquire 1 permit
rateLimiter.acquire(3); // Acquire 3 permits
// Try to acquire permits (non-blocking)
boolean acquired = rateLimiter.tryAcquire(); // Try to acquire 1 permit
boolean acquired3 = rateLimiter.tryAcquire(3); // Try to acquire 3 permits
boolean acquiredWithTimeout = rateLimiter.tryAcquire(2, TimeUnit.SECONDS); // With timeout
// Dynamic rate adjustment
rateLimiter.setRate(10.0); // Change to 10 permits per second
double currentRate = rateLimiter.getRate();
// Bursty rate limiter (allows bursts up to specified amount)
RateLimiter bursty = RateLimiter.create(2.0, 5, TimeUnit.SECONDS);
// 2 permits/second, but can accumulate up to 10 permits over 5 seconds
// Practical usage example
public class ApiClient {
private final RateLimiter rateLimiter = RateLimiter.create(10.0); // 10 requests/second
public String makeApiCall(String endpoint) throws Exception {
rateLimiter.acquire(); // Wait for permit
return httpClient.get(endpoint);
}
public Optional<String> tryApiCall(String endpoint) throws Exception {
if (rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
return Optional.of(httpClient.get(endpoint));
} else {
return Optional.absent(); // Rate limit exceeded
}
}
}Synchronization primitive with boolean conditions, providing more flexible locking than traditional synchronized blocks.
import com.google.common.util.concurrent.Monitor;
public class BoundedBuffer<T> {
private final Monitor monitor = new Monitor();
private final Monitor.Guard notEmpty = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return size > 0;
}
};
private final Monitor.Guard notFull = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return size < capacity;
}
};
private final T[] buffer;
private final int capacity;
private int size = 0;
private int head = 0;
private int tail = 0;
@SuppressWarnings("unchecked")
public BoundedBuffer(int capacity) {
this.capacity = capacity;
this.buffer = (T[]) new Object[capacity];
}
public void put(T item) throws InterruptedException {
monitor.enterWhen(notFull); // Wait until buffer not full
try {
buffer[tail] = item;
tail = (tail + 1) % capacity;
size++;
} finally {
monitor.leave();
}
}
public T take() throws InterruptedException {
monitor.enterWhen(notEmpty); // Wait until buffer not empty
try {
T item = buffer[head];
buffer[head] = null;
head = (head + 1) % capacity;
size--;
return item;
} finally {
monitor.leave();
}
}
public boolean tryPut(T item, long timeout, TimeUnit unit) throws InterruptedException {
if (monitor.enterWhen(notFull, timeout, unit)) {
try {
buffer[tail] = item;
tail = (tail + 1) % capacity;
size++;
return true;
} finally {
monitor.leave();
}
}
return false; // Timeout
}
public T tryTake(long timeout, TimeUnit unit) throws InterruptedException {
if (monitor.enterWhen(notEmpty, timeout, unit)) {
try {
T item = buffer[head];
buffer[head] = null;
head = (head + 1) % capacity;
size--;
return item;
} finally {
monitor.leave();
}
}
return null; // Timeout
}
}Framework for managing application services with lifecycle management and state transitions.
import com.google.common.util.concurrent.Service;
import com.google.common.util.concurrent.AbstractService;
import com.google.common.util.concurrent.ServiceManager;
// Custom service implementation
public class DatabaseService extends AbstractService {
private DatabaseConnection connection;
@Override
protected void doStart() {
try {
connection = new DatabaseConnection();
connection.connect();
notifyStarted(); // Signal successful start
} catch (Exception e) {
notifyFailed(e); // Signal start failure
}
}
@Override
protected void doStop() {
try {
if (connection != null) {
connection.close();
}
notifyStopped(); // Signal successful stop
} catch (Exception e) {
notifyFailed(e); // Signal stop failure
}
}
public void executeQuery(String sql) {
// Service must be running to execute operations
checkRunning();
connection.execute(sql);
}
private void checkRunning() {
if (state() != State.RUNNING) {
throw new IllegalStateException("Service is not running: " + state());
}
}
}
// Service management
Service databaseService = new DatabaseService();
Service webService = new WebService();
Service cacheService = new CacheService();
// Individual service management
databaseService.startAsync();
databaseService.awaitRunning(30, TimeUnit.SECONDS); // Wait for start
// Service state monitoring
Service.State state = databaseService.state();
boolean isRunning = (state == Service.State.RUNNING);
// Add state listeners
databaseService.addListener(new Service.Listener() {
@Override
public void starting() {
System.out.println("Database service starting...");
}
@Override
public void running() {
System.out.println("Database service is running");
}
@Override
public void stopping(Service.State from) {
System.out.println("Database service stopping from state: " + from);
}
@Override
public void terminated(Service.State from) {
System.out.println("Database service terminated from state: " + from);
}
@Override
public void failed(Service.State from, Throwable failure) {
System.err.println("Database service failed: " + failure.getMessage());
}
}, MoreExecutors.directExecutor());
// Managing multiple services
ServiceManager serviceManager = new ServiceManager(
Arrays.asList(databaseService, webService, cacheService));
// Start all services
serviceManager.startAsync();
serviceManager.awaitHealthy(60, TimeUnit.SECONDS); // Wait for all to be healthy
// Stop all services
serviceManager.stopAsync();
serviceManager.awaitStopped(30, TimeUnit.SECONDS);
// Service dependencies and health checks
Map<Service, Long> startupTimes = serviceManager.startupTimes();
ImmutableMultimap<Service.State, Service> servicesByState = serviceManager.servicesByState();Provides a set of locks that can be used to stripe synchronization across multiple objects.
import com.google.common.util.concurrent.Striped;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
// Striped locks for better concurrency
Striped<Lock> striped = Striped.lock(16); // 16 locks in the stripe
Striped<ReadWriteLock> readWriteStriped = Striped.readWriteLock(16);
// Use with object keys
public class StripedCounter {
private final ConcurrentMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();
private final Striped<Lock> striped = Striped.lock(32);
public void increment(String key) {
Lock lock = striped.get(key); // Get lock for this key
lock.lock();
try {
AtomicInteger counter = counters.get(key);
if (counter == null) {
counter = new AtomicInteger(0);
counters.put(key, counter);
}
counter.incrementAndGet();
} finally {
lock.unlock();
}
}
public int get(String key) {
Lock lock = striped.get(key);
lock.lock();
try {
AtomicInteger counter = counters.get(key);
return counter != null ? counter.get() : 0;
} finally {
lock.unlock();
}
}
}
// Bulk operations with ordered locking (avoids deadlocks)
public void transfer(String fromKey, String toKey, int amount) {
Iterable<Lock> locks = striped.bulkGet(Arrays.asList(fromKey, toKey));
for (Lock lock : locks) {
lock.lock();
}
try {
// Perform transfer operation
AtomicInteger from = counters.get(fromKey);
AtomicInteger to = counters.get(toKey);
if (from != null && from.get() >= amount) {
from.addAndGet(-amount);
if (to == null) {
counters.put(toKey, new AtomicInteger(amount));
} else {
to.addAndGet(amount);
}
}
} finally {
// Release in reverse order
List<Lock> locksList = Lists.newArrayList(locks);
Collections.reverse(locksList);
for (Lock lock : locksList) {
lock.unlock();
}
}
}Enhanced atomic operations and utilities for concurrent programming.
import com.google.common.util.concurrent.AtomicDouble;
import com.google.common.util.concurrent.AtomicLongMap;
import com.google.common.util.concurrent.Uninterruptibles;
// Atomic operations on doubles
AtomicDouble atomicDouble = new AtomicDouble(0.0);
double result = atomicDouble.addAndGet(3.14);
atomicDouble.compareAndSet(3.14, 2.71);
// Atomic map for counters
AtomicLongMap<String> counters = AtomicLongMap.create();
long count = counters.incrementAndGet("requests"); // Atomic increment
counters.addAndGet("bytes", 1024); // Atomic add
long total = counters.get("requests"); // Get current value
Map<String, Long> snapshot = counters.asMap(); // Snapshot of all values
// Uninterruptible operations
public void robustOperation() {
// Sleep that can't be interrupted
Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS);
// Join that can't be interrupted
Thread workerThread = new Thread(task);
workerThread.start();
Uninterruptibles.joinUninterruptibly(workerThread);
// Future get that can't be interrupted
Future<String> future = executor.submit(callable);
String result = Uninterruptibles.getUninterruptibly(future);
// Future get with timeout that can't be interrupted
String resultWithTimeout = Uninterruptibles.getUninterruptibly(
future, 30, TimeUnit.SECONDS);
}Utilities for testing concurrent code and timing behavior.
import com.google.common.testing.FakeTicker;
import com.google.common.base.Ticker;
// Testing with fake time
public class ConcurrentServiceTest {
@Test
public void testRateLimiting() {
FakeTicker ticker = new FakeTicker();
RateLimiter rateLimiter = RateLimiter.create(1.0, ticker); // 1 permit/second
// Should allow first request immediately
assertTrue(rateLimiter.tryAcquire());
// Should deny second request immediately
assertFalse(rateLimiter.tryAcquire());
// Advance time by 1 second
ticker.advance(1, TimeUnit.SECONDS);
// Should allow request after time advance
assertTrue(rateLimiter.tryAcquire());
}
@Test
public void testServiceLifecycle() throws Exception {
TestService service = new TestService();
// Test initial state
assertEquals(Service.State.NEW, service.state());
// Test successful start
service.startAsync();
service.awaitRunning(1, TimeUnit.SECONDS);
assertEquals(Service.State.RUNNING, service.state());
// Test successful stop
service.stopAsync();
service.awaitTerminated(1, TimeUnit.SECONDS);
assertEquals(Service.State.TERMINATED, service.state());
}
}Guava's concurrency utilities provide powerful abstractions for asynchronous programming, resource management, and thread coordination that go well beyond Java's standard concurrency libraries while maintaining compatibility and ease of use.
Install with Tessl CLI
npx tessl i tessl/maven-com-google-guava--guava