What is Thread Local?
- ThreadLocal is a Java utility class that provides thread-local variables.
- Each thread accessing a ThreadLocal variable has its own, independent copy of the variable.
- Changes made to a ThreadLocal variable in one thread do not affect the value of the same variable in another thread.
- This is particularly useful when working in multi-threaded environments, such as parallel test execution, where shared resources may lead to race conditions or inconsistent results.
- Playwright allows parallel execution of tests across multiple threads (browsers, contexts, or pages).
- If all threads share the same Playwright objects (like BrowserContext or Page), one test could unintentionally interfere with another.
- To avoid this, we use ThreadLocal storage so that: Each thread maintains its own Browser, Context, and Page. No data leakage or object collision happens across test executions.
- For objects like Playwright, Browser, BrowserContext, and Page, you create ThreadLocal instances.
- This ensures each thread holds its own version of these objects.
- When a test thread starts, it initializes its own Playwright, Browser, Context, and Page inside its ThreadLocal storage.
- Other threads will do the same, but with their own isolated objects.
- During test execution, instead of directly accessing shared variables, you get the instance from the ThreadLocal.
- This guarantees that the actions being performed belong only to that thread’s browser/page.
- Once the test finishes, you remove the objects from ThreadLocal storage.
- This prevents memory leaks and ensures resources are freed.
In Playwright Java, ThreadLocal
is often used in test automation frameworks (like TestNG or JUnit) to ensure thread safety when running tests in parallel. ThreadLocal
allows each thread to maintain its own isolated instance of a variable — like the Playwright Browser
, Page
, or BrowserContext
.
Use Case:
When you run tests in parallel, sharing a single instance of Browser
or Page
across threads can cause data corruption or flaky tests. ThreadLocal
ensures each test thread uses its own Browser
, Context
, and Page
.
Example: Thread-safe Playwright with ThreadLocal in Java
Website to be automated for showing ThreadLocal concept:
https://www.google.com
PlaywrightFactory.java
Below is the PlaywrightFactory class which consists of Threadlocal class objects of type Playwright, Browser, BrowserContext, and Page classes. It also consists of getter and setter methods of these objects. In the last objects are closed by close() and removed by remove()
import com.microsoft.playwright.*; public class PlaywrightFactory { private static ThreadLocal<Playwright> tlPlaywright = new ThreadLocal<>(); private static ThreadLocal<Browser> tlBrowser = new ThreadLocal<>(); private static ThreadLocal<BrowserContext> tlContext = new ThreadLocal<>(); private static ThreadLocal<Page> tlPage = new ThreadLocal<>(); public static void initPlaywright() { tlPlaywright.set(Playwright.create()); } public static void initBrowser() { BrowserType.LaunchOptions options = new BrowserType.LaunchOptions().setHeadless(false); tlBrowser.set(getPlaywright().chromium().launch(options)); } public static void initContext() { tlContext.set(getBrowser().newContext()); } public static void initPage() { tlPage.set(getContext().newPage()); } public static Playwright getPlaywright() { return tlPlaywright.get(); } public static Browser getBrowser() { return tlBrowser.get(); } public static BrowserContext getContext() { return tlContext.get(); } public static Page getPage() { return tlPage.get(); } public static void closeAll() { getPage().close(); getContext().close(); getBrowser().close(); getPlaywright().close(); // Remove from ThreadLocal tlPage.remove(); tlContext.remove(); tlBrowser.remove(); tlPlaywright.remove(); } }
import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.microsoft.playwright.*; public class SampleTest { @BeforeMethod public void setUp() { PlaywrightFactory.initPlaywright(); PlaywrightFactory.initBrowser(); PlaywrightFactory.initContext(); PlaywrightFactory.initPage(); } @Test public void testGoogleSearch() { Page page = PlaywrightFactory.getPage(); page.navigate("https://www.google.com"); System.out.println("Page Title: " + page.title()); } @AfterMethod public void tearDown() { PlaywrightFactory.closeAll(); } }
Parallel Execution
To run parallel tests safely:
Use TestNG with
<parallel="methods">
or<parallel="classes">
Each test thread will get its own isolated Playwright
Page
due toThreadLocal
Suggested Posts:
1. Playwright with TestNG Integration
2. Automate Login Page in Playwright
3. Comma Selectors in Playwright
4. Trace Viewer in Playwright
5. Handle GET API in Playwright