Understanding Thread Safety: Memory Isolation, ThreadLocal, Locks, and CAS in Java
The article explains why thread safety concerns memory rather than threads, describes how local stack variables, ThreadLocal, locks, and CAS provide isolation and consistency in Java concurrency, and compares optimistic and pessimistic locking strategies with illustrative code examples.
Thread safety is not about protecting threads themselves but about protecting the shared memory they access; without proper isolation, data in the heap can be unintentionally modified by other threads.
Each process has a private heap and a per‑thread stack. Local variables are allocated on the stack, so they are inherently thread‑private and safe.
When data must be shared across threads, ThreadLocal can give each thread its own copy of a variable stored in the heap, preventing interference.
double avgScore(double[] scores) {
double sum = 0;
for (double score : scores) {
sum += score;
}
int count = scores.length;
double avg = sum / count;
return avg;
}In the example above, sum, count, and avg are local variables placed on each thread's stack, so concurrent executions do not clash.
class StudentAssistant {
ThreadLocal<String> realName = new ThreadLocal<>();
ThreadLocal<Double> totalScore = new ThreadLocal<>();
String determineDegree() {
double score = totalScore.get();
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "E";
}
double determineOptionalcourseScore() {
double score = totalScore.get();
if (score >= 90) return 10;
if (score >= 80) return 20;
if (score >= 70) return 30;
if (score >= 60) return 40;
return 60;
}
}ThreadLocal stores a separate value for each thread, effectively copying the data N times so that each thread works on its own copy.
Making a variable immutable (e.g., final double passScore = 60;) also guarantees safety because no thread can modify it.
The three isolation techniques—private stack variables, ThreadLocal copies, and immutable constants—are all ways to “avoid the heavy lifting” of synchronization.
When shared mutable data must be accessed, a lock (mutex) is required: a thread must acquire the lock before modifying the data and release it afterward.
class ClassAssistant {
double totalScore = 60;
final Lock lock = new Lock();
void addScore(double score) {
lock.obtain();
totalScore += score;
lock.release();
}
void subScore(double score) {
lock.obtain();
totalScore -= score;
lock.release();
}
}Locks provide pessimistic protection but incur overhead; for low contention scenarios, Compare‑And‑Swap (CAS) offers an optimistic alternative.
CAS works by reading the current value, performing the operation only if the value has not changed, and retrying if it has; it can suffer from the ABA problem, which is mitigated by version counters.
In summary, the article classifies thread‑safety solutions into isolation (stack, ThreadLocal, immutability), pessimistic locking, and optimistic CAS, advising the appropriate technique based on contention levels.
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.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.
