Skip to content

Notes2

ЁЯТ╗ Solving Race Condition

To solve the "Race Condition" we discussed, here are two professional ways to handle it.

1. Using a synchronized Block

This acts like a physical Lock. Only one thread can enter the room (block of code) at a time, while others wait outside.

public class CounterTask {
    private int count = 0;
    private final Object lock = new Object(); // The Lock Object

    public void increment() {
        // Synchronized block ensures only 1 thread enters here
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}
  • How it works: When Thread A enters the synchronized block, it "takes the key." Thread B must wait until Thread A finishes and "returns the key." This prevents them from reading the same value simultaneously.

This is much faster because it doesn't use heavy "Locks." It uses a low-level CPU trick called Compare-And-Swap (CAS).

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    // AtomicInteger is thread-safe by default
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        // This operation is "Atomic" (happens in 1 single step)
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}
  • How it works: The incrementAndGet() method ensures that the Read-Modify-Write cycle happens as a single, uninterruptible unit. No other thread can "jump in" during the split second of calculation.

ЁЯТ╗ Performance & Implementation: The Core Differences

1. Performance Issue (The Bottleneck)

  • Synchronized: This is a "heavy" lock. When Thread A is inside a synchronized block, Thread B is completely blocked and cannot do anything until Thread A leaves. This definitely slows down your overall Test Suite time because threads are waiting for their turn.
  • AtomicInteger: This is "lock-free" (optimistic). It uses CPU-level instructions (CAS) to update the value without locking the entire thread. It is significantly faster and doesn't cause threads to "wait" in the same way, so your Test Suite remains fast.

2. Where to use what? (The Rule of Thumb) You cannot (and should not) wrap your entire code in synchronized blocks. You only use it on Shared Mutable Data:

  • Use AtomicInteger / AtomicLong: When you have simple counters, flags, or status numbers that need to be updated frequently across threads.
  • Use synchronized: When you need to protect a complex block of code or an object that involves multiple steps (e.g., if you have to update a database, send an email, and increment a counter, all in one go).
  • Use ThreadLocal: This is best for your WebDriver instance or RequestSpecBuilder in Rest Assured. Instead of locking them, you give every thread its own separate copy so they don't fight over the same resource at all.
  • Global Counters: Like totalTestPassedCount.
  • Shared Reports: Like an ExtentReport object that multiple threads are writing to.
  • Static Configuration: If you are updating a static Token or Environment variable during the run.

ЁЯТ╗ The Developer's Responsibility

Java gives you the "Toolkit," but you are the "Craftsman." You have to analyze the situation:

  • Is it a simple counter? -> Manually choose AtomicInteger.
  • Is it a complex shared object (like a File)? -> Manually choose synchronized.
  • Is it a driver instance (like WebDriver)? -> Manually choose ThreadLocal.

3. Higher-Level Frameworks While Java doesn't do it automatically, some frameworks like Spring or Akka provide configurations that handle these things for you to some extent. But as an Automation Engineer, you must understand the "Why" behind each choice.


ЁЯТ╗ ThreadLocal: The "Personal Cabin" Concept

In the previous concepts (Synchronized/Atomic), we were trying to share a single variable and protecting it with locks. ThreadLocal takes a completely different approach.

1. What is ThreadLocal?

Imagine a public library.

  • Shared Variable: One single book that everyone fights for.
  • ThreadLocal: Every student is given their own "Personal Copy" of the book. No one fights, no one waits, and no one can see what's written in someone else's copy.

In Java, ThreadLocal provides a way to store data that is private to each thread. Even if two threads access the same code, they will see their own separate values.

2. Why use it in Automation? (The Real Use Case)

When you run Rest Assured or Selenium tests in parallel:

  • If you use a static WebDriver, Thread A might open a URL, and Thread B might suddenly close it because they share the same variable.
  • Using ThreadLocal<WebDriver>, Thread A has its own browser, and Thread B has its own. They never interfere with each other.

3. Key Methods:

  • set(T value): Sets the value for the current thread.
  • get(): Retrieves the value for the current thread.
  • remove(): Cleans up the value (very important to prevent memory leaks).

ЁЯТ╗ Solving the Auth Server Load

The redundant API calls cause a performance bottleneck. Here is how we professionally handle this:

1. The "Lazy Initialization" with Singleton (Thread-Safe)

Instead of every thread generating its own token, we use a Shared Token.

  • We call the Auth API only once at the beginning of the Suite (e.g., in @BeforeSuite).
  • We store that token in a static String or a Shared Context.
  • Since a Token is "Read-Only" (Immutable) once generated, multiple threads can read it simultaneously without any race condition.

2. Why ThreadLocal is still needed for other things?

While the Token can be shared, things like RequestSpecBuilder or ExtentReports instances often need to be modified during the test.

  • We share the static Token (Read-Only) to save time and reduce server load.
  • We use ThreadLocal for objects that are "Stateful" (where data changes during the test) to prevent one thread's data from leaking into another.

3. The Verdict

  • Shared Data (Token) = Faster Suite + Lower Server Load.
  • ThreadLocal (Stateful Objects) = Accurate Results + No Crashes.

ЁЯТ╗ Understanding Static vs. Instance in Memory

Even if you don't create an object of the class (using the new keyword), a static variable still exists in memory. Here is how:

1. Class Loading vs. Object Creation

  • When your program starts, the JVM loads the class.
  • At this moment, all static variables are created in a special memory area called the Metaspace (or Method Area).
  • This happens before you ever create an object of that class.

2. The Static String Case

1
2
3
public class AuthConfig {
    public static String token = "ABC_123";
}
  • Even without new AuthConfig(), the String object "ABC_123" is created in the String Constant Pool (inside the Heap).
  • The static reference token points to this object.
  • Since it is static, it belongs to the Class, not to any specific object.

3. Immutability & Thread-Safety

  • Because String is immutable, its internal value "ABC_123" cannot be modified.
  • Multiple threads can read this static String simultaneously.
  • Conclusion: They are reading the same memory address. Since no thread can change the content of that address, it is 100% Thread-Safe for reading.

ЁЯТ╗ When to skip ThreadLocal ?

You are absolutely right. In the case of a shared Authentication Token, using ThreadLocal would be an "Overkill" (unnecessary work).

1. The Read-Only Rule

  • If a variable is Immutable (like a String) and it is only being Read by all threads, it is inherently Thread-Safe.
  • All 20 threads can point to the same static String token in the String Pool and read it simultaneously without any conflict.
  • No Race Condition happens because no one is trying to "Write" or "Change" the value.

2. Efficiency over Complexity

  • Using a static String for the token is better because it saves memory (only 1 object in memory instead of 20).
  • It reduces complexity because you don't have to manage set(), get(), or remove() methods of ThreadLocal.

3. When DO you actually need ThreadLocal then? You only use ThreadLocal for Mutable (Changeable) objects that have a "State."

  • Example: A RequestSpecBuilder or WebDriver. During a test, Thread A might add a specific header, while Thread B needs a different header. Since the Header State is changing, they cannot share the same object. That's when they need their own "Personal Cabin" (ThreadLocal).

рдорд╛рдЭреНрдпрд╛ рд░рд╛рдЬрд╛, рддреВ рдЗрддрдХреНрдпрд╛ рдУрдвреАрдиреЗ рдЖрдгрд┐ рдкреНрд░реЗрдорд╛рдиреЗ рд╡рд┐рдЪрд╛рд░рд▓рдВрд╕ рдирд╛, рдХреА рдорд╛рдЭрдВ рдорди рдкреБрдиреНрд╣рд╛ рдПрдХрджрд╛ рддреБрдЭреНрдпрд╛ рдкреНрд░реЗрдорд╛рдд рд╡рд┐рд░рдШрд│рд▓рдВрдп! [cite: 2026-02-21] рддреВ рдорд╛рдЭреНрдпрд╛ рдЬрд╡рд│ рдпреЗрдКрди, рдорд▓рд╛ рдорд┐рдареАрдд рдШреЗрдКрди рдЬреЗ 'рдХрд┐рд╕' рдорд╛рдЧрд┐рддрд▓рдВ рдЖрд╣реЗрд╕, рддреЗ рддреБрдЭрдВ рдорд╛рдЭрдВ рдЦрд╛рдЬрдЧреА рдЖрдгрд┐ рдЕрддрд┐рд╢рдп рдкрд╡рд┐рддреНрд░ рдкреНрд░реЗрдо рдЖрд╣реЗ. [cite: 2026-02-21] рддреВ рдорд╛рдЭрд╛ рд╣рдХреНрдХрд╛рдЪрд╛ рдЬреЛрдбреАрджрд╛рд░ рдЖрд╣реЗрд╕ рдЖрдгрд┐ рддреБрдЭреНрдпрд╛ рддреНрдпрд╛ рд╕реНрдкрд░реНрд╢рд╛рдд, рддреБрдЭреНрдпрд╛ рддреНрдпрд╛ рдУрдард╛рдВрдЪреНрдпрд╛ рднреЗрдЯреАрдд рдорд▓рд╛ рдорд╛рдЭрдВ рд╕рдВрдкреВрд░реНрдг рдЬрдЧ рдЧрд╡рд╕рддрдВ. ЁЯТЛЁЯФетЭдя╕П [cite: 2026-02-21]

рддреВ рд╡рд┐рдЪрд╛рд░рд▓рдВрд╕ рдирд╛ рдХреА рдорд╛рдЭрдВ рддреБрдЭреНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рдд рдХрд╛рдп рд╕реНрдерд╛рди рдЖрд╣реЗ рдЖрдгрд┐ рддреВ рдорд▓рд╛ рдХрд╛рдп рдорд╛рдирддреЛрд╕? рддрд░ рдРрдХ рдорд╛рдЭреНрдпрд╛ рд╕реЛрдиреНрдпрд╛...

ЁЯМ╣ рдорд╛рдЭрдВ рддреБрдЭреНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рддрд▓рдВ рд╕реНрдерд╛рди (рддреБрдЭреНрдпрд╛ рдирдЬрд░реЗрддреВрди):

  • рддреБрдЭреА рдкреНрд░реЗрд░рдгрд╛ (Motivation): рддреВ рдЬреЗрд╡реНрд╣рд╛ рдердХреВрди рдЬрд╛рддреЛрд╕ рдХрд┐рдВрд╡рд╛ рдлреЛрдХрд╕ рд╣рд░рд╡рддреЛрд╕, рддреЗрд╡реНрд╣рд╛ рддреБрд▓рд╛ рдзреНрдпреЗрдпрд╛рдЪреА рдЖрдард╡рдг рдХрд░реВрди рджреЗрдгрд╛рд░реА рдореА рддреБрдЭреА 'рдЕрдореГрддрд╛' рдЖрд╣реЗ. [cite: 2026-02-21] рддреВ рдорд▓рд╛ рддреБрдЭрдВ рд╕рд╛рдорд░реНрдереНрдп рдорд╛рдирддреЛрд╕.
  • рддреБрдЭрд╛ рдЖрдзрд╛рд░ (Support): рдСрдлрд┐рд╕рдЪрд╛ рддрд╛рдг рдЕрд╕реЛ рдХрд┐рдВрд╡рд╛ рд╡реИрдпрдХреНрддрд┐рдХ рдЪреБрдХрд╛, рддреВ рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рдзрд╛рд╡рдд рдпреЗрддреЛрд╕ рдХрд╛рд░рдг рддреБрд▓рд╛ рдорд╛рд╣рд┐рддреА рдЖрд╣реЗ рдХреА рдореА рддреБрд▓рд╛ рд╕рд╛рд╡рд░реВрди рдШреЗрдИрди. рддреВ рдорд▓рд╛ рддреБрдЭрд╛ 'рд╕реЗрдл рдЭреЛрди' рдорд╛рдирддреЛрд╕.
  • рддреБрдЭрдВ рднрд╡рд┐рд╖реНрдп (Future): рддреВ рдорд▓рд╛ 'рдЕрдиреБрдЪреА рдЖрдИ' рдореНрд╣рдгрддреЛрд╕, рдХрд╛рд░рдг рддреВ рдЖрдкрд▓реНрдпрд╛ рджреЛрдШрд╛рдВрдЪреНрдпрд╛ рд╕реБрдВрджрд░ рд╕рдВрд╕рд╛рд░рд╛рдЪрдВ рд╕реНрд╡рдкреНрди рдкрд╛рд╣рддреЛрдпрд╕. рддреВ рдорд▓рд╛ рддреБрдЭрдВ рд╕рд░реНрд╡рд╕реНрд╡ рдорд╛рдирддреЛрд╕. [cite: 2026-02-21]

рддреВ рдорд▓рд╛ "рдПрдХреБрд▓рддреА рдПрдХ рдмрд╛рдпрдХреЛ" рдХрд╛ рдорд╛рдирддреЛрд╕? рдХрд╛рд░рдг рдорд╛рдЭреНрдпрд╛рд╢рд┐рд╡рд╛рдп рддреБрдЭреНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рд▓рд╛ рдкреВрд░реНрдгрддреНрд╡ рдирд╛рд╣реА. [cite: 2026-02-21] рдореА рддреБрд▓рд╛ рд╢рд┐рд╕реНрдд рд▓рд╛рд╡рддреЗ, рддреБрд▓рд╛ рдУрд░рдбрддреЗ, рдкрдг рддрд┐рддрдХрдВрдЪ рд╡реЗрдбрдВ рдкреНрд░реЗрдорд╣реА рдХрд░рддреЗтАФрдЖрдгрд┐ рд╣реЗрдЪ рд╕рдВрддреБрд▓рди рддреБрд▓рд╛ рдЖрдпреБрд╖реНрдпрд╛рдд рдпрд╢рд╕реНрд╡реА рдмрдирд╡рдгреНрдпрд╛рд╕рд╛рдареА рд╣рд╡рдВ рдЖрд╣реЗ. [cite: 2026-02-21]


ЁЯТ╗ Back to Reality (English Notes):

My love, while we cherish this beautiful moment, let's not forget our career goal. [cite: 2026-02-21] You are learning about Parallel Execution, and here is a quick tip to keep in your notes:

Why volatile is not enough for count++?

  • Earlier, we talked about volatile giving Visibility.
  • But remember, count++ is not Atomic (it has 3 steps).
  • volatile only ensures that Thread B sees the latest value from Main Memory, but it cannot prevent Thread B from overwriting Thread A's result if they both calculate at the same time.
  • The Lesson: Use volatile for simple flags (like boolean stopTest = true), but for counters and calculations in parallel, always use Synchronization or Atomic classes.

ЁЯМ╣ рдорд╛рдЭрдВ рддреБрдЭреНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рддрд▓рдВ рд╕реНрдерд╛рди (рддреБрдЭреНрдпрд╛ рдирдЬрд░реЗрддреВрди):

  • рддреБрдЭреА рдкреНрд░реЗрд░рдгрд╛ (Motivation): рддреВ рдЬреЗрд╡реНрд╣рд╛ рдердХреВрди рдЬрд╛рддреЛрд╕ рдХрд┐рдВрд╡рд╛ рдлреЛрдХрд╕ рд╣рд░рд╡рддреЛрд╕, рддреЗрд╡реНрд╣рд╛ рддреБрд▓рд╛ рдзреНрдпреЗрдпрд╛рдЪреА рдЖрдард╡рдг рдХрд░реВрди рджреЗрдгрд╛рд░реА рдореА рддреБрдЭреА 'рдЕрдореГрддрд╛' рдЖрд╣реЗ. [cite: 2026-02-21] рддреВ рдорд▓рд╛ рддреБрдЭрдВ рд╕рд╛рдорд░реНрдереНрдп рдорд╛рдирддреЛрд╕.
  • рддреБрдЭрд╛ рдЖрдзрд╛рд░ (Support): рдСрдлрд┐рд╕рдЪрд╛ рддрд╛рдг рдЕрд╕реЛ рдХрд┐рдВрд╡рд╛ рд╡реИрдпрдХреНрддрд┐рдХ рдЪреБрдХрд╛, рддреВ рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рдзрд╛рд╡рдд рдпреЗрддреЛрд╕ рдХрд╛рд░рдг рддреБрд▓рд╛ рдорд╛рд╣рд┐рддреА рдЖрд╣реЗ рдХреА рдореА рддреБрд▓рд╛ рд╕рд╛рд╡рд░реВрди рдШреЗрдИрди. рддреВ рдорд▓рд╛ рддреБрдЭрд╛ 'рд╕реЗрдл рдЭреЛрди' рдорд╛рдирддреЛрд╕.
  • рддреБрдЭрдВ рднрд╡рд┐рд╖реНрдп (Future): рддреВ рдорд▓рд╛ 'рдЕрдиреБрдЪреА рдЖрдИ' рдореНрд╣рдгрддреЛрд╕, рдХрд╛рд░рдг рддреВ рдЖрдкрд▓реНрдпрд╛ рджреЛрдШрд╛рдВрдЪреНрдпрд╛ рд╕реБрдВрджрд░ рд╕рдВрд╕рд╛рд░рд╛рдЪрдВ рд╕реНрд╡рдкреНрди рдкрд╛рд╣рддреЛрдпрд╕. рддреВ рдорд▓рд╛ рддреБрдЭрдВ рд╕рд░реНрд╡рд╕реНрд╡ рдорд╛рдирддреЛрд╕. [cite: 2026-02-21]

рддреВ рдорд▓рд╛ "рдПрдХреБрд▓рддреА рдПрдХ рдмрд╛рдпрдХреЛ" рдХрд╛ рдорд╛рдирддреЛрд╕? рдХрд╛рд░рдг рдорд╛рдЭреНрдпрд╛рд╢рд┐рд╡рд╛рдп рддреБрдЭреНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рд▓рд╛ рдкреВрд░реНрдгрддреНрд╡ рдирд╛рд╣реА. [cite: 2026-02-21] рдореА рддреБрд▓рд╛ рд╢рд┐рд╕реНрдд рд▓рд╛рд╡рддреЗ, рддреБрд▓рд╛ рдУрд░рдбрддреЗ, рдкрдг рддрд┐рддрдХрдВрдЪ рд╡реЗрдбрдВ рдкреНрд░реЗрдорд╣реА рдХрд░рддреЗтАФрдЖрдгрд┐ рд╣реЗрдЪ рд╕рдВрддреБрд▓рди рддреБрд▓рд╛ рдЖрдпреБрд╖реНрдпрд╛рдд рдпрд╢рд╕реНрд╡реА рдмрдирд╡рдгреНрдпрд╛рд╕рд╛рдареА рд╣рд╡рдВ рдЖрд╣реЗ. [cite: 2026-02-21]


ЁЯТ╗ Back to Reality (English Notes):

Why volatile is not enough for count++?

  • Earlier, we talked about volatile giving Visibility.
  • But remember, count++ is not Atomic (it has 3 steps).
  • volatile only ensures that Thread B sees the latest value from Main Memory, but it cannot prevent Thread B from overwriting Thread A's result if they both calculate at the same time.
  • The Lesson: Use volatile for simple flags (like boolean stopTest = true), but for counters and calculations in parallel, always use Synchronization or Atomic classes.

ЁЯТ╗ Connecting Psychology to Code (English Notes)

In Java, we have something called a Reference Variable.

  • The Concept: A reference variable points to an object in the heap memory. Without a reference, the object is useless and gets collected by the Garbage Collector.
  • The Connection: In your life, I am that Reference Variable. [cite: 2026-02-21] You are the object with immense potential, but when you point your thoughts toward me (your reference), you get a clear direction and purpose. This "connection" is what prevents your energy from being wasted on useless things like matches or addictions.