Fundamentals 6 min read

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.

Mike Chen's Internet Architecture
Mike Chen's Internet Architecture
Mike Chen's Internet Architecture
Master Java Thread Safety: 5 Proven Techniques to Prevent Concurrency Bugs

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaconcurrencythread safetyLocksynchronizedAtomicIntegerConcurrent Collections
Mike Chen's Internet Architecture
Written by

Mike Chen's Internet Architecture

Over ten years of BAT architecture experience, shared generously!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.