Concurrency Utilities in Java: Callable, ReentrantLock, and Synchronized Explained

Returning Values from Threads with Callable

The Callable interfcae allows threads to return computation results and throw checked exceptions. Unlike Runnable, it features a call() method designed for task execution. To retreive the result, wrap the Callable instance in a FutureTask and pass it to a Thread.

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class ComputationTask implements Callable<Integer> {
    @Override
    public Integer call() {
        return 42 * 2;
    }

    public static void main(String[] args) throws Exception {
        ComputationTask task = new ComputationTask();
        FutureTask<Integer> wrapper = new FutureTask<>(task);
        
        new Thread(wrapper).start();
        
        // Blocking call to retrieve the result
        Integer value = wrapper.get();
        System.out.println("Computed value: " + value);
    }
}

Explicit Loccking with ReentrantLock

ReentrantLock provides advanced locking capabilities compared to the implicit synchronized keyword. It supports interruptible locking, attempting to lock without blocking, and fairness policies. Always release the lock in a finally block to prevent deadlocks.

import java.util.concurrent.locks.ReentrantLock;

class CounterService {
    private final ReentrantLock accessLock = new ReentrantLock();
    private int total = 0;

    public void addValue(int amount) {
        accessLock.lock();
        try {
            total += amount;
        } finally {
            accessLock.unlock();
        }
    }

    public static void main(String[] args) {
        CounterService service = new CounterService();
        service.addValue(100);
    }
}

Implicit Synchronization with synchronized

The synchronized keyword provides built-in monitor lock support. It can be applied to entire methods or specific blocks of code to ensure that only one thread accesses the critical section at a time.

class SharedResource {
    private int counter = 0;

    public void increase() {
        synchronized (this) {
            counter++;
        }
    }

    public int getCurrentValue() {
        return counter;
    }

    public static void main(String[] args) throws InterruptedException {
        SharedResource resource = new SharedResource();

        for (int i = 0; i < 5; i++) {
            new Thread(resource::increase).start();
        }

        Thread.sleep(1000);
        System.out.println("Current counter: " + resource.getCurrentValue());
    }
}

Tags: java Concurrency multithreading Callable ReentrantLock

Posted on Thu, 13 Aug 2026 16:39:28 +0000 by eurozaf