Achieving thread safety in Java involves ensuring that shared resources (variables, objects, data structures, etc.) are accessed in a way that does not lead to data inconsistency or corruption when multiple threads are accessing them concurrently.
Here are several approaches given below to achieve thread safety in Java,
Synchronization
Using synchronized Keyword
Here use the synchronized keyword to define critical sections where only one thread can execute at a time.
Methods can be synchronized (synchronized void method() { ... }) or specific blocks can be synchronized (synchronized (obj) { ... }).
public class Counter {
private int count;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final Count (Synchronized Method): " + counter.getCount());
}
}
Atomic Classes
Using Atomic Classes from java.util.concurrent.atomic.
Provides lock-free thread-safe operations on single variables (AtomicInteger, AtomicLong, etc.).
Useful for simple tasks like incrementing and updating variables.
Example-
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final Count (AtomicInteger): " + counter.getCount());
}
}
Thread-Local Variables
Using ThreadLocal from java.lang
Provides thread-local variables, where each thread has its own independently initialized copy of the variable.
This is useful when each thread needs its own instance of a variable to avoid synchronization.
Example-
public class ThreadLocalExample {
private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 0);
public void increment() {
threadLocalValue.set(threadLocalValue.get() + 1);
}
public int getValue() {
return threadLocalValue.get();
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalExample example = new ThreadLocalExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
example.increment();
System.out.println("Thread 1 - Value: " + example.getValue());
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
example.increment();
System.out.println("Thread 2 - Value: " + example.getValue());
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final Value (Thread-Local): " + example.getValue());
}
}
Using Immutable Objects
Objects whose state cannot be modified after their creation.
Immutable objects are inherently thread safe, since they cannot change their state once they have been constructed.
Example-
public final class ImmutableCounter {
private final int count;
public ImmutableCounter(int count) {
this.count = count;
}
public int getCount() {
return count;
}
}
Choosing the Right Approach
Performance vs. Safety- Consider the trade-off between performance and thread-safety. More robust communication mechanisms (e.g., ReentrantLock) can provide better performance in highly contentious situations compared to synchronized blocks.
Concurrency Level- Select an option based on the concurrency level and complexity of the application.
Achieving thread safety in Java requires choosing the appropriate synchronization mechanism or method based on the specific needs and features of the shared resources and application concurrency model Each method has advantages and trade-offs, and the choice depends on the nature of resources as labor, weakness, and concurrency level and concurrency.
We use cookies to ensure you have the best browsing experience on our website. By using our site, you
acknowledge that you have read and understood our
Cookie Policy &
Privacy Policy.
Thread Safety Achieving
Achieving thread safety in Java involves ensuring that shared resources (variables, objects, data structures, etc.) are accessed in a way that does not lead to data inconsistency or corruption when multiple threads are accessing them concurrently.
Here are several approaches given below to achieve thread safety in Java,
Synchronization
Using
synchronizedKeywordsynchronized void method() { ... }) or specific blocks can be synchronized (synchronized (obj) { ... }).Atomic Classes
Using Atomic Classes from
java.util.concurrent.atomic.Example-
Thread-Local Variables
Using
ThreadLocalfromjava.langExample-
Using Immutable Objects
Example-
Choosing the Right Approach
Achieving thread safety in Java requires choosing the appropriate synchronization mechanism or method based on the specific needs and features of the shared resources and application concurrency model Each method has advantages and trade-offs, and the choice depends on the nature of resources as labor, weakness, and concurrency level and concurrency.
Also, Read: Explain with example to handle synchronization in Java.