In Java, working with threads is implemented through the Thread, Runnable classes, and packages from java.util.concurrent. Various synchronization mechanisms are used to ensure thread safety:
synchronized) allow multiple threads to access shared data without race conditions.ReentrantLock, Semaphore, AtomicInteger, ConcurrentHashMap) provide more flexible synchronization methods.Example of synchronization:
public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
Example using atomic classes:
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
Does the volatile keyword guarantee thread safety when incrementing an int counter?
Answer: No. volatile only ensures visibility of the value between threads, but not atomicity of operations. Incrementing is a non-atomic operation (count++ consists of reading, incrementing, and writing), so data loss can occur. For thread-safe incrementing, synchronization or classes like AtomicInteger are needed.
volatile int count = 0; // count++ is not thread-safe!
Story
In an online store, a bonus account was updated using a volatile counter. Under load, thousands of users were ordering products, and some bonuses were lost due to thread race on count++.
Story
An employee used a regular ArrayList as a shared buffer between a producer and consumer in a multithreaded application, resulting in ConcurrentModificationException. The solution was to use synchronized blocks or replace it with CopyOnWriteArrayList.
Story
In payment processing, the developer performed the operation "check and deduct amount" through two unrelated methods. Under high load, this led to double charging until atomic transactions and locks were implemented.