Master Java Thread Safety: 5 Proven Techniques to Prevent Concurrency Bugs
This article explains five common ways to ensure thread safety in Java, covering synchronized blocks, the Lock interface, atomic variables, concurrent collections, and ThreadLocal usage, each with clear code examples and practical advice.
Ensuring thread safety is a frequent interview topic in Java; this article explains five common ways to achieve it.
1. Use synchronized blocks
Apply the synchronized keyword to control concurrent access, ensuring only one thread executes the block or method at a time.
public class Counter {
private int count;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int getCount() {
return count;
}
}2. Use the Lock interface
Similar to synchronized, the Lock interface (e.g., ReentrantLock) provides more flexibility and finer‑grained control.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}3. Use atomic variables
Atomic classes such as AtomicInteger guarantee atomic operations even when accessed by multiple threads.
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public void decrement() {
count.decrementAndGet();
}
public int getCount() {
return count.get();
}
}4. Use thread‑safe container classes
Java provides concurrent collections like ConcurrentHashMap, ConcurrentLinkedQueue, and CopyOnWriteArrayList to avoid concurrency issues when sharing data structures.
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapThreadSafeExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("A", i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("B", i);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(map); // {A=9999, B=9999}
}
}5. Avoid shared mutable state
Minimize shared resources; use ThreadLocal to give each thread its own instance, such as a SimpleDateFormat object.
public class ThreadLocalExample {
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
executorService.submit(() -> {
String date = new ThreadLocalExample().formatDate(new Date());
System.out.println("Thread: " + Thread.currentThread().getName() + " formatted date: " + date);
});
}
executorService.shutdown();
}
private String formatDate(Date date) {
SimpleDateFormat dateFormat = dateFormatThreadLocal.get();
return dateFormat.format(date);
}
}Each method has its own trade‑offs; choose the one that best fits your scenario while being careful to avoid deadlocks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Mike Chen's Internet Architecture
Over ten years of BAT architecture experience, shared generously!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
