Java Tutorial · Advanced Java · Lesson 36 of 37
Java Threads and Concurrency
Run work in parallel with executors, virtual threads and thread-safe counters.
Threads and executors
A thread runs code concurrently. Instead of creating threads by hand, submit tasks to an ExecutorService and get results back as Futures:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws Exception {
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> results = new ArrayList<>();
for (String city : List.of("London", "Tokyo", "Lima")) {
results.add(pool.submit(() -> {
Thread.sleep(100); // pretend to call a weather API
return city + ": sunny";
}));
}
for (Future<String> f : results) {
System.out.println(f.get());
}
}
}
}
Output
London: sunny Tokyo: sunny Lima: sunny
Run this on your computer — see “Get Started” for setup.
Virtual threads (Java 21)
Virtual threads are lightweight threads managed by the JVM — you can run thousands of blocking tasks (HTTP calls, database queries) without a huge thread pool. Executors.newVirtualThreadPerTaskExecutor() gives you one per task.
Thread safety
When threads share data, unsynchronised updates get lost. Use AtomicInteger, synchronized, locks, or concurrent collections like ConcurrentHashMap:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) {
AtomicInteger safe = new AtomicInteger();
int[] unsafe = {0};
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
for (int i = 0; i < 4; i++) {
pool.submit(() -> {
for (int j = 0; j < 10_000; j++) {
safe.incrementAndGet();
unsafe[0]++; // race condition!
}
});
}
}
System.out.println("atomic: " + safe.get());
System.out.println("unsafe is 40000? " + (unsafe[0] == 40_000) + " (usually false)");
}
}
Output
atomic: 40000 unsafe is 40000? false (usually false)
Run this on your computer — see “Get Started” for setup.
Practise: Thread-safe counter.
Test yourself
Which class gives you a thread-safe integer counter without locks?
IntegerAtomicIntegervolatile intSafeInt
Show answer
Answer: AtomicInteger
Practice next
- Thread-Safe Counting with ExecutorService · coding problem