境下如何遵守線程安全規(guī)則并各線程創(chuàng)建獨(dú)立實(shí)例?)
Playwright Java 在多線程環(huán)境下如何遵守線程安全規(guī)則并各線程創(chuàng)建獨(dú)立實(shí)例【免費(fèi)下載鏈接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/pl/playwright如果你在 Java 里用多個(gè)線程同時(shí)驅(qū)動(dòng) Playwright比如并行測(cè)試多個(gè)瀏覽器、或在多線程程序中各自打開頁(yè)面首先面對(duì)的一條官方規(guī)則是Playwright Java 不是線程安全的見 threading-java.md。原文的表述是Playwright Java is not thread safe, i.e. all its methods as well as methods on all objects created by it (such as BrowserContext, Browser, Page etc.) are expected to be called on the same thread where the Playwright object was created or proper synchronization should be implemented to ensure only one thread calls Playwright methods at any given time.也就是說所有方法——包括Playwright以及由它創(chuàng)建的BrowserContext、Browser、Page等對(duì)象上的方法——都應(yīng)預(yù)期在“創(chuàng)建該P(yáng)laywright對(duì)象的那個(gè)線程”上被調(diào)用如果確實(shí)要從其他線程調(diào)用就必須自己實(shí)現(xiàn)同步保證任一時(shí)刻只有一個(gè)線程在調(diào)用 Playwright 方法。規(guī)則同時(shí)給出了合法的多線程做法在各自獨(dú)立的線程上創(chuàng)建多個(gè)Playwright實(shí)例。原文“its okay to create multiple Playwright instances each on its own thread.” 本文就按這條路徑展開每個(gè)線程創(chuàng)建自己的Playwright實(shí)例、啟動(dòng)自己的瀏覽器進(jìn)程、在自己的實(shí)例上執(zhí)行操作線程之間不共享任何 Playwright 對(duì)象。準(zhǔn)備條件按 intro-java.mdInstallation文檔Java 端的使用前提是通過 Maven 引入依賴pom.xml中加入com.microsoft.playwright:playwright文檔中的版本號(hào)是構(gòu)建期占位符%%VERSION%%請(qǐng)?zhí)鎿Q為你要使用的實(shí)際發(fā)行版本不要直接照抄Java 8 或更高版本編譯器配置為 source/target 1.8系統(tǒng)要求Windows 11 / Windows Server 2019 / WSLmacOS 14 (Sonoma) 或更高Debian 12 / 13Ubuntu 22.04 / 24.04 / 26.04x86-64 或 arm64。首次運(yùn)行腳本時(shí)會(huì)自動(dòng)下載 Playwright 包并安裝 Chromium、Firefox、WebKit 的瀏覽器二進(jìn)制如需修改該行為文檔指向 browsers.md 的 install-browsers 一節(jié)。主路徑每個(gè)線程創(chuàng)建獨(dú)立的 Playwright 實(shí)例threading-java.md 給出的示例正是這個(gè)場(chǎng)景的完整實(shí)現(xiàn)為三種瀏覽器chromium、webkit、firefox各創(chuàng)建一個(gè)線程每個(gè)線程在自己的run()里完成“創(chuàng)建實(shí)例 → 啟動(dòng)瀏覽器 → 打開頁(yè)面 → 截圖”的全流程package org.example; import com.microsoft.playwright.*; import java.nio.file.Paths; import static java.util.Arrays.asList; public class PlaywrightThread extends Thread { private final String browserName; private PlaywrightThread(String browserName) { this.browserName browserName; } public static void main(String[] args) throws InterruptedException { // Create separate playwright thread for each browser. for (String browserName: asList(chromium, webkit, firefox)) { Thread thread new PlaywrightThread(browserName); thread.start(); } } Override public void run() { try (Playwright playwright Playwright.create()) { BrowserType browserType getBrowserType(playwright, browserName); Browser browser browserType.launch(); Page page browser.newPage(); page.navigate(https://playwright.dev/); page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get(user-agent- browserName .png))); } } private static BrowserType getBrowserType(Playwright playwright, String browserName) { switch (browserName) { case chromium: return playwright.chromium(); case webkit: return playwright.webkit(); case firefox: return playwright.firefox(); default: throw new IllegalArgumentException(); } } }這個(gè)結(jié)構(gòu)對(duì)應(yīng)文檔描述的“每個(gè)實(shí)例啟動(dòng)自己的瀏覽器進(jìn)程并對(duì)它運(yùn)行測(cè)試”Playwright.create()在run()內(nèi)調(diào)用即每個(gè)線程只在自己身上創(chuàng)建實(shí)例try-with-resources保證線程結(jié)束后該實(shí)例及其資源被釋放chromium()/webkit()/firefox()都從線程私有的playwright對(duì)象上取BrowserType隨后launch()啟動(dòng)的是該線程自己的瀏覽器進(jìn)程三個(gè)線程之間沒有任何共享的Playwright、Browser或Page對(duì)象因此不觸發(fā)“跨線程調(diào)用同一對(duì)象”的不安全用法。文檔給出的運(yùn)行方式來自 intro-java.mdmvn compile exec:java -D exec.mainClassorg.example.PlaywrightThread-D exec.mainClass的值替換為你的主類名。如何判斷運(yùn)行成功文檔沒有給出固定輸出日志作為成功條件示例代碼本身定義了可核對(duì)的產(chǎn)物每個(gè)線程會(huì)把https://playwright.dev/的頁(yè)面截圖寫到工作目錄下文件名按瀏覽器名拼接為user-agent-chromium.png、user-agent-webkit.png、user-agent-firefox.png。三個(gè)文件都存在就說明三個(gè)線程各自完成了“創(chuàng)建獨(dú)立實(shí)例 → 啟動(dòng)瀏覽器 → 導(dǎo)航 → 截圖”的完整流程。瀏覽器默認(rèn)以 headless 模式運(yùn)行即不會(huì)彈出窗口這不影響截圖生成。同步 API 的配套規(guī)則等待事件用 waitForTimeout 而不是 Thread.sleep線程安全之外同文檔還說明了一條直接影響多線程程序?qū)懛ǖ男袨橥?Playwright API 中所有事件只在 Playwright 運(yùn)行其消息循環(huán)時(shí)才會(huì)派發(fā)而這發(fā)生在你調(diào)用任意 API 方法時(shí)如果調(diào)用棧上沒有任何活躍的 Playwright 調(diào)用事件不會(huì)派發(fā)。由此得到一個(gè)常見坑如果你在某個(gè)線程里調(diào)用Thread.sleep()暫停執(zhí)行休眠期間不會(huì)有事件被觸發(fā)。若希望在程序暫停期間仍然派發(fā)來自瀏覽器的事件例如持續(xù)接收onResponse回調(diào)應(yīng)使用Page.waitForTimeout()或Frame.waitForTimeout()page.onResponse(response - System.out.println(response.url())); page.navigate(https://playwright.dev); System.out.println(-- did navigate --); // Block current thread for 60s and ensure the events are dispatched. page.waitForTimeout(60_000);文檔同時(shí)給出的等待事件的一般建議是需要等待某個(gè)事件時(shí)用waitFor*系列方法。在“每個(gè)線程一個(gè)實(shí)例”的模式下這條規(guī)則同樣適用于每個(gè)線程自己的Page??蛇x分支JUnit 并行測(cè)試下的線程安全如果你的目標(biāo)是并行跑 JUnit 測(cè)試而不是自寫多線程程序junit-java.mdJUnit (experimental) 文檔給出了同一原則在測(cè)試框架中的落地方式測(cè)試類加UsePlaywright注解啟用 Playwright fixtures其中playwrightfixture 的描述是“Playwright instance is shared between tests running on the same thread.”——即同一線程內(nèi)共享實(shí)例而文檔針對(duì)并行的建議是“Since it is not safe to use same Playwright objects from multiple threads without extra synchronization we recommend you create Playwright instance per thread and use it on that thread exclusively.” 與主路徑的規(guī)則一致JUnit 默認(rèn)單線程順序執(zhí)行JUnit 5.3 起可改為并行執(zhí)行。文檔給出的并行配置是junit.jupiter.execution.parallel.enabled true junit.jupiter.execution.parallel.mode.default same_thread junit.jupiter.execution.parallel.mode.classes.default concurrent junit.jupiter.execution.parallel.config.strategydynamic junit.jupiter.execution.parallel.config.dynamic.factor0.5含義按文檔說明每個(gè)類內(nèi)的測(cè)試保持順序執(zhí)行多個(gè)類在并行線程上執(zhí)行最大線程數(shù)等于 CPU 核數(shù)的一半dynamic.factor0.5。驗(yàn)證方式與文檔中示例一致并行的測(cè)試類各自使用Page pagefixture每個(gè)測(cè)試一個(gè)隔離的Page/BrowserContext瀏覽器在測(cè)試間共享以節(jié)省資源例如shouldClickButton斷言page.evaluate(result)等于Clicked、shouldCheckTheBox斷言 checkbox 的checked為true這些斷言通過即說明各線程獨(dú)立取用 fixture 工作正常。限制與邊界本文依據(jù)的線程安全規(guī)則來自 threading-java.md它描述的是同步 Java API 的行為“在同一線程調(diào)用或自行實(shí)現(xiàn)同步”這一前提適用于所有由Playwright創(chuàng)建的對(duì)象BrowserContext、Browser、Page等。JUnit fixture 一節(jié)來自標(biāo)注為 experimental 的 junit-java.md并行配置依賴 JUnit 5.3 的并行執(zhí)行能力。事件派發(fā)只發(fā)生在有活躍 Playwright 調(diào)用時(shí)因此“暫停線程但繼續(xù)收事件”必須走waitFor*路徑而不是Thread.sleep()?!久赓M(fèi)下載鏈接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/pl/playwright創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考