Notes on Java Concurrency

Mustafa Atik
2 min readJun 12, 2021

Here I collect my notes about the topic of Java concurrency. This is probably not a complete or useful article for anyone except for me. But you can still check out the reference list to find more complete sources.

Thread.class can be used to run a code block implementing in an Runnableinterface.

Runnable task = () -> {
String threadName = Thread.currentThread().getName();
System.out.println("The thread name is " + threadName);
};

task.run();

Thread thread = new Thread(task);
thread.start();

When you have more threads, the Executor can be used to manage them. For example, you can have a pool of threads. When there is a task, one of these threads can execute it, if all the threads are busy, then the task waits in the queue.

Runnable task = () -> {
String threadName = Thread.currentThread().getName();
System.out.println("The thread name is " + threadName);
};

ExecutorService executor = Executor.newFixedThreadPool(3);

executor.submit(task);
executor.submit(task);
executor.submit(task);
executor.submit(task);
executor.shutdown(); // shut down after all the tasks have compleeted.

Callable & Future

If there is a need to return a result from a thread task, then use Callable interface instead of Runnable. The return value is represented via Future

Callable<Double> task = () -> {
return Math.random();
};

ExecutorService executor = Executors.newFixedThreadPool(3);

Future<Double> result = executor.submit(task);
Double d = result.get(2, TimeUnit.SECONDS); // wait for 2 seconds at most
System.out.println(d);

Synchronization and locks

Intuitively the code below is expected to increase val to 50, as many times as the increment method is invoked. But in reality, val may not be 50 because of race condition. That is, while one thread increasing it to 5, for example, another thread may be doing exactly the same work.

static int val;
static void increment() {
val = val + 1;
}

public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 50; i++) {
executor.submit(() -> {
increment();
});
}
executor.shutdown();
System.out.println(val);
}

If increment is only invoked executed by a single thread at a time, then this race condition will not occur. To do so, we can wrap the code pieces with synchronized so that only one thread can enter within it.

int val;
void increment() {
synchronized (this) {
val = val + 1;
}
}

Or locks can be used as follows:

static ReentrantLock lock = new ReentrantLock();
static void increment() {
lock.lock();
val = val + 1;
lock.unlock();
}

Semaphore

Limit the number of threads acquiring permits to run a code block.

Semaphore semaphore = new Semaphore(3);
void increment() {
final boolean permit = semaphore.tryAcquire(1, TimeUnit.MILLISECONDS);
if (permit) {
Thread.sleep(1000);
synchronized (this) {
val = val + 1;
}
semaphore.release();
}
}

Topics

References

--

--

Mustafa Atik

You can find my notes I take when learning something new or reading, watching. So, they only help me to refresh and remember what I’ve consumed.