हम Java 21 वर्चुअल थ्रेड्स पर स्विच किए और Postgres के लिए TPC-C में एक deadlock मिला

Java 21 के नए virtual threads मौजूदा Java code में subtle deadlock risks उजागर कर रहे हैं, खासकर जब `synchronized` और `Object.wait()` जैसे पुराने synchronization primitives का उपयोग JDBC connection pools जैसी libraries के अंदर किया जाता है। Commenters बताते हैं कि ये constructs carrier threads को “pin” कर सकते हैं और सीमित underlying thread pool को exhaust कर सकते हैं, जिससे वे programs टूट सकते हैं जो कई OS threads के साथ सुरक्षित थे। आम राय यह है कि virtual threads I/O-heavy workloads के लिए promising हैं, लेकिन अभी हर जगह drop-in replacement नहीं हैं; इसलिए developers को libraries audit करनी चाहिए, modern concurrency utilities (locks, semaphores, concurrent collections) को प्राथमिकता देनी चाहिए, और व्यापक adoption से पहले runtime fixes के लिए tune करना या इंतज़ार करना चाहिए.

डेडलॉक की प्रकृति

  • आम सहमति है कि virtual threads ने “जादुई रूप से” किसी नई तरह के deadlock को नहीं बनाया; benchmark ने नए constraints के तहत एक मौजूदा design issue को उजागर किया।
  • Deadlock इसलिए हुआ क्योंकि virtual threads c3p0 connection pool code के अंदर synchronized + Object.wait का उपयोग करते हुए block हो रहे थे, जिससे carrier threads exhausted हो गए।
  • लेख में दिया गया fix — DB connections को Semaphore से guard करना — virtual threads को connection उपलब्ध होने तक c3p0 में प्रवेश करने से रोकता है, इसलिए वे pool के अंदर नहीं बल्कि VT-aware code में block होते हैं।
  • कई commenters इस बात पर जोर देते हैं कि यह एक generic JDBC/connection-pool issue है, PostgreSQL-विशिष्ट नहीं।

Virtual threads, pinning, और forward progress

  • Virtual threads को unmount नहीं किया जा सकता जब वे:
    • synchronized blocks/methods के अंदर चल रहे हों, या
    • native/foreign code, या Object.wait जैसी कुछ legacy blocking calls निष्पादित कर रहे हों।
  • इन मामलों में carrier (OS) thread “pinned” हो जाता है; यदि ऐसे कई calls हों, तो VT scheduler का carrier pool (default max 256, jdk.virtualThreadScheduler.maxPoolSize के माध्यम से configurable) exhausted हो सकता है।
  • कुछ लोग स्पष्ट करते हैं कि Object.wait monitor को release भी करता है और native code में implement होता है, इसलिए समस्या केवल synchronized की नहीं है, बल्कि यह भी कि wait स्वयं VT scheduling के साथ cooperates नहीं करता।
  • चिंता lock correctness से कम और तब “forward progress” खोने से अधिक है जब सभी carriers block हो जाएँ।

“Drop-in replacement” बनाम नई semantics

  • Documentation और कुछ messaging ने virtual threads को अधिकतर drop-in के रूप में प्रस्तुत किया; दूसरे लोग adoption guide की ओर इशारा करते हैं, जो सभी platform threads को blindly VTs से बदलने के खिलाफ स्पष्ट चेतावनी देता है।
  • एक पक्ष: यदि आपके design को carriers से अधिक simultaneously runnable threads चाहिए, तो design fragile था; VT बस उसे उजागर करता है।
  • दूसरा पक्ष: पिछला code इस मान्यता पर सही था कि OS हमेशा और threads spawn कर सकता है; hard carrier cap introduce करने से system behavior silently बदल जाता है और ऐसा code टूट सकता है।
  • इस बात पर असहमति है कि documentation ने deadlocks के बारे में पर्याप्त चेतावनी दी थी या सिर्फ “scalability” issues के बारे में।

चर्चित workarounds और best practices

  • synchronized/wait/notify patterns को बदलें:
    • java.util.concurrent locks, semaphores, और conditions से।
    • producer–consumer patterns के लिए concurrent queues से।
  • synchronized के अंदर लंबे-running या blocking operations से बचें; VTs के बिना भी इसे खराब शैली मानें।
  • carrier-thread pools और configuration का उपयोग करें:
    • जहाँ उपयुक्त हो jdk.virtualThreadScheduler.maxPoolSize बढ़ाएँ।
    • CPU-bound या native calls को platform-thread executors पर offload करें।
  • -Djdk.tracePinnedThreads जैसे diagnostic tools का उपयोग करके problem areas खोजें।
  • कुछ लोग c3p0 जैसे पुराने pools से HikariCP जैसे actively maintained ones पर जाने का सुझाव देते हैं, हालांकि modern pools भी अभी VTs के अनुसार adjust कर रहे हैं।

Virtual threads की परिपक्वता और भविष्य के fixes

  • बहुत से लोग virtual threads को शक्तिशाली मानते हैं, लेकिन अभी “set and forget” नहीं; वे IO-bound, modern code के लिए सबसे अच्छे हैं जो legacy primitives से बचता है।
  • कई लोग नोट करते हैं कि synchronized/wait behavior एक ज्ञात, अस्थायी implementation limitation है और JDK team इसे VT-friendly बनाने पर काम कर रहा है।
  • कुछ सलाह देते हैं कि व्यापक adoption से पहले ऐसे future LTS का इंतज़ार करें जहाँ synchronized और Object.wait virtual threads के साथ पूरी तरह cooperate करें, खासकर उन codebases में जिनमें कई third-party libraries हों।

तुलनाएँ और meta-discussion

  • Go, Erlang, Node.js, Python async, और .NET के thread pool/async से तुलनाएँ की गईं: blocking और non-blocking models को मिलाने पर इन सभी में शुरू में quirks रहे।
  • Side discussions यह प्रश्न उठाती हैं कि क्या green/M:N threading कभी OS threads के आसपास बने ecosystems में पूरी तरह transparent हो सकती है।
  • छोटे tangents HN title के editorializing और technical posts पर obvious AI-generated hero images के व्यापक dislike को cover करते हैं।