8 Proven Java Techniques to Ensure Thread‑Safe Data in Concurrent Apps
This article outlines eight practical Java approaches—including stateless design, immutability, safe publication, volatile fields, synchronized blocks, explicit locks, CAS operations, and ThreadLocal—to guarantee data safety in multithreaded environments, explaining each concept and providing concise code examples.
Ensuring data safety under concurrency is a daily challenge for developers; overlooking it can cause data anomalies. Based on practical experience, the following techniques can be used.
1. Stateless
When no shared resources are accessed, data safety issues disappear.
public class NoStatusService {
public void add(String status) {
System.out.println("add status:" + status);
}
public void update(String status) {
System.out.println("update status:" + status);
}
}2. Immutable
If shared resources are immutable, they cannot be altered by concurrent threads.
public class NoChangeService {
public static final String DEFAULT_NAME = "abc";
public void add(String status) {
System.out.println("add status:" + status);
}
}3. Safe Publication
When a class has shared resources but does not expose them publicly, thread‑safety is maintained.
public class SafePublishService {
private String name;
public String getName() {
return name;
}
public void add(String status) {
System.out.println("add status:" + status);
}
}4. volatile
For a shared flag that only requires visibility, the volatile keyword ensures that changes are seen by all threads.
public class FlagService {
public volatile boolean flag = false;
public void change() {
if (flag) {
System.out.println("return");
return;
}
flag = true;
System.out.println("change");
}
}5. synchronized
JDK’s built‑in synchronization can be applied at method or block level; block synchronization is preferred for finer granularity.
public class SyncService {
private int age = 1;
public synchronized void add(int i) {
age = age + i;
System.out.println("age:" + age);
}
public void update(int i) {
synchronized (this) {
age = age + i;
System.out.println("age:" + age);
}
}
}6. Lock
Explicit locks (e.g., ReentrantLock) provide re‑entrancy and read‑write capabilities, though they require manual unlocking.
public class LockService {
private ReentrantLock reentrantLock = new ReentrantLock();
public int age = 1;
public void add(int i) {
try {
reentrantLock.lock();
age = age + i;
System.out.println("age:" + age);
} finally {
reentrantLock.unlock();
}
}
}7. CAS
Compare‑and‑swap uses atomic CPU instructions; in Java it is exposed via Unsafe or the java.util.concurrent.atomic package, avoiding explicit locks.
public class AtomicService {
private AtomicInteger atomicInteger = new AtomicInteger();
public int add(int i) {
return atomicInteger.getAndAdd(i);
}
}8. ThreadLocal
ThreadLocal gives each thread its own copy of a variable, eliminating contention; remember to call remove() to prevent memory leaks.
public class ThreadLocalService {
private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public void add(int i) {
Integer integer = threadLocal.get();
threadLocal.set(integer == null ? 0 : integer + i);
}
}In summary, these eight techniques provide a toolbox for achieving thread‑safe data handling; the choice depends on the specific scenario.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
