CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-guava--guava

Comprehensive Java library providing essential utilities, immutable collections, caching, and concurrency tools for modern Java development.

Pending
Overview
Eval results
Files

concurrency.mddocs/

Concurrency

Enhanced concurrency utilities including listenable futures, rate limiting, improved executors, and synchronization primitives that extend Java's concurrent programming capabilities.

Package: com.google.common.util.concurrent

ListenableFuture

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);

Combining Futures

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);

Exception Handling with Futures

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);

Timeouts and Scheduling

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);

ListeningExecutorService

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);

RateLimiter

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
        }
    }
}

Monitor

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
    }
}

Service Framework

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();

Striped Locks

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();
        }
    }
}

Atomic Operations and Utilities

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);
}

Testing Concurrent Code

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

docs

basic-utilities.md

caching.md

collections.md

concurrency.md

graph-api.md

hash-math.md

immutable-collections.md

index.md

io-utilities.md

other-utilities.md

tile.json