ServerSocket --ye ek class h and server side pr connection accept krne ke liye ye ek port pr listening krta h
socket--ye bhi ek class h ye client and server ke nich ek end to end connection setup krta h
PrintWriter(class) -- ye bhi ek class h iska use output stream se text related data ko likhne ke liye
BufferedReader(class)--input stream se character based data line by line pdhne ke liye
InputStreamReader(class)->bite stream(yani input stream ) ko character stream me bdlne ke liye
Thread Pool-->थ्रेड पूल, थ्रेड्स का एक संग्रह (collection) होता है जिन्हें पहले से बनाकर रखा जाता है। जब आपके सर्वर के पास कोई काम (task) आता है, जैसे कि एक नया क्लाइंट कनेक्शन, तो सर्वर नया थ्रेड बनाने के बजाय, इस पूल से एक तैयार थ्रेड को वह काम सौंप देता है।
काम पूरा होने के बाद, वह थ्रेड नष्ट नहीं होता, बल्कि पूल में वापस आ जाता है और अगले काम का इंतजार करता है। इस प्रक्रिया को थ्रेड रीयूज़ (Thread Reuse) कहते हैं।
why we use thread PooL-->
- प्रदर्शन में सुधार (Improved Performance) तेज़ प्रतिक्रिया (Faster Response): थ्रेड बनाने (Creation) और नष्ट करने (Destruction) में CPU और मेमोरी का समय लगता है। इसे ओवरहेड (Overhead) कहते हैं। थ्रेड पूल में थ्रेड्स पहले से ही मौजूद होते हैं, इसलिए काम तुरंत शुरू हो जाता है, जिससे सर्वर की प्रतिक्रिया गति (response time) बहुत तेज़ हो जाती है।
थ्रेड रीयूज़: थ्रेड्स को बार-बार इस्तेमाल किया जाता है, जिससे क्रिएशन और डिस्ट्रक्शन का ओवरहेड खत्म हो जाता है।
- संसाधन प्रबंधन (Resource Management) सीमित संसाधन: प्रत्येक थ्रेड मेमोरी लेता है। यदि आपके पास एक साथ हज़ारों क्लाइंट्स कनेक्ट हो जाते हैं, और आप हर एक के लिए नया थ्रेड बनाते हैं, तो आपके सिस्टम की मेमोरी खत्म हो सकती है, जिससे सर्वर क्रैश हो सकता है (Out of Memory Error)।
नियंत्रण: थ्रेड पूल आपको यह सीमा निर्धारित करने की अनुमति देता है कि एक समय में अधिकतम कितने थ्रेड्स सक्रिय (active) हो सकते हैं (जैसे 10 या 50)। इससे आपका सिस्टम ओवरलोड होने से बच जाता है।
- बेहतर नियंत्रण (Better Control) आपके सभी थ्रेड्स एक जगह से प्रबंधित होते हैं। ExecutorService इंटरफ़ेस आपको पूल को आसानी से शटडाउन (shutdown) करने की अनुमति देता है जब सर्वर बंद हो रहा हो।
थ्रेड पूल की कार्यप्रणाली (How a Thread Pool Works) ⚙️ थ्रेड पूल मुख्य रूप से तीन चीज़ों पर निर्भर करता है:
-
पूल (The Pool) यह पहले से बने हुए थ्रेड्स का सेट होता है। आपके कोड में, Executors.newFixedThreadPool(10) 10 थ्रेड्स का पूल बनाता है।
-
टास्क क्यू (Task Queue) जब पूल के सभी थ्रेड्स व्यस्त होते हैं, लेकिन कोई नया काम (जैसे 11वां क्लाइंट कनेक्शन) आ जाता है, तो वह काम इस कतार (Queue) में डाल दिया जाता है। यह काम तब तक इंतज़ार करता है जब तक पूल का कोई थ्रेड फ्री न हो जाए।
-
एक्ज़ीक्यूटर (The Executor) यह वह क्लास है जो काम को पूल में मौजूद थ्रेड्स को सौंपने का लॉजिक संभालती है। Java में, यह काम ExecutorService इंटरफ़ेस करता है।
स्टेप्स:
सर्वर ExecutorService बनाता है।
क्लाइंट कनेक्ट होता है और ClientHandler टास्क बनता है।
यह टास्क pool.execute(task) के ज़रिए पूल को दिया जाता है।
पूल देखता है कि क्या कोई थ्रेड फ्री है?
हाँ: फ्री थ्रेड तुरंत टास्क उठा लेता है और उसे चलाता है।
नहीं: टास्क को क्यू में डाल दिया जाता है।
जब थ्रेड टास्क पूरा कर लेता है, तो वह क्यू से अगला टास्क उठाता है, या क्यू के खाली होने पर निष्क्रिय (idle) होकर पूल में वापस इंतज़ार करने लगता है।
यही कारण है कि मल्टी-थ्रेडेड सर्वर बनाने के लिए ExecutorService का उपयोग करना सबसे अच्छा तरीका माना जाता है।
ExecutorService --ye ek interface h jo concurrent package kaa hissa h and iska use multithreaded server bnane me kiya jata h
newFixedThreadPool(int nThreads) (Static Factory Method) यह Executors क्लास की एक स्टेटिक मेथड है।
क्या है?: यह एक ExecutorService (थ्रेड पूल) ऑब्जेक्ट को बनाता है और उसे रिटर्न करता है।
"Fixed" का मतलब: इसका मतलब है कि यह पूल एक निश्चित संख्या में थ्रेड्स बनाएगा, जो कि nThreads पैरामीटर में पास किया जाता है। एक बार बन जाने के बाद, पूल में थ्रेड्स की संख्या बदलती नहीं है।
अगर 10 थ्रेड्स का पूल है (THREAD_POOL_SIZE = 10), और 15 टास्क आते हैं, तो 10 थ्रेड्स काम करेंगे, और 5 टास्क क्यू (Queue) में इंतज़ार करेंगे।
यह सर्वर को ओवरलोड होने से बचाता है।
serverSocket.accept();--> ye serversocket class ki ek inbuilt method h ye connection request ko accept krta h
.execute() method --to execute thread pool .shutdown() method--to close thread pool
INTERFACE V/S CLASS -->
क्लास (Class) बनाम इंटरफ़ेस (Interface) ⚔️ दोनों ही ब्लूप्रिंट हैं, पर वे अलग-अलग चीज़ों के ब्लूप्रिंट हैं।
- क्लास: "यह क्या है" का ब्लूप्रिंट (What It Is - Blueprint of Identity and Implementation) क्लास एक ऑब्जेक्ट का पूरा और विस्तृत ब्लूप्रिंट है। यह बताता है:
डेटा (Variables): ऑब्जेक्ट के पास कौन-कौन सी विशेषताएँ (properties) होंगी।
व्यवहार (Methods): ऑब्जेक्ट क्या-क्या काम कर सकता है।
कार्यान्वयन (Implementation): कैसे काम करेगा (यानी, मेथड के अंदर का कोड)।
उदाहरण: एक Car क्लास बताती है कि कार में 4 टायर होंगे (data), और वह चल सकती है (method)। साथ ही, यह भी बताती है कि चलने के लिए उसे इंजन को शुरू करना होगा (मेथड का कोड/Implementation)।
- इंटरफ़ेस: "यह क्या कर सकता है" का ब्लूप्रिंट (What It Can Do - Blueprint of Contract or Capability) इंटरफ़ेस एक कॉन्ट्रैक्ट (Contract) या क्षमता (Capability) का ब्लूप्रिंट है। यह सिर्फ इतना बताता है कि क्या काम करना है, यह कभी नहीं बताता कि वह काम कैसे करना है।
यह केवल घोषणा (Declaration) करता है, कार्यान्वयन (Implementation) नहीं करता।
इंटरफ़ेस में केवल मेथड के सिग्नेचर (नाम, पैरामीटर और रिटर्न टाइप) होते हैं, उनके अंदर कोई कोड नहीं होता (Java 8 के पहले)।
उदाहरण: एक CanFly इंटरफ़ेस बस कहता है कि "जो कोई भी मुझे लागू करेगा (implement करेगा), उसके पास एक fly() मेथड ज़रूर होनी चाहिए।"
CanFly इंटरफ़ेस void fly(); Aeroplane क्लास CanFly को लागू करती है और fly() के अंदर इंजन-आधारित उड़ने का कोड लिखती है। Bird क्लास CanFly को लागू करती है और fly() के अंदर पंख-आधारित उड़ने का कोड लिखती है।
Export to Sheets 3. इंटरफ़ेस की ज़रूरत क्यों है? (Why Do We Need Interfaces?) 💡 इंटरफ़ेस का होना दो बहुत बड़ी प्रोग्रामिंग समस्याओं को हल करता है:
A. मल्टीपल इनहेरिटेंस की समस्या (Solving Multiple Inheritance) Java जानबूझकर एक क्लास को एक से ज़्यादा क्लास को एक्सटेंड (Extend) करने की अनुमति नहीं देता है। इसे डायमंड प्रॉब्लम (Diamond Problem) कहते हैं।
क्यों? क्योंकि अगर Class C दो पैरेंट क्लास (A और B) को एक्सटेंड करे, और दोनों में समान नाम की मेथड हो, तो Class C को पता नहीं चलेगा कि किसे चलाना है। इससे कोड में भ्रम (ambiguity) पैदा होती है।
इंटरफ़ेस यह समस्या हल कर देता है। एक क्लास एक से ज़्यादा इंटरफ़ेस को इम्प्लीमेंट (Implement) कर सकती है।
Class C implements Interface X, Interface Y
ऐसा इसलिए संभव है क्योंकि इंटरफ़ेस में केवल घोषणा होती है, कोई कोड नहीं। कोई भ्रम नहीं है, क्योंकि Class C को पता है कि खुद को दोनों मेथड का कोड लिखना है।
B. ढीला युग्मन (Loose Coupling) और पॉलीमॉर्फिज्म (Polymorphism) यह इंटरफ़ेस का सबसे बड़ा फायदा है।
ढील ा युग्मन (Loose Coupling): इंटरफ़ेस किसी भी प्रोग्राम को जानने की अनुमति देता है कि कोई ऑब्जेक्ट क्या कर सकता है, उसे यह जाने बिना कि वह क्या है या वह काम कैसे करता है।
आपके सर्वर में: ExecutorService को केवल इतना पता है कि वह Runnable टाइप के किसी भी ऑब्जेक्ट को चला सकता है। उसे इस बात की चिंता नहीं है कि वह ऑब्जेक्ट ClientHandler है या DataProcessor। यह लचीलापन (Flexibility) देता है।
पॉलीमॉर्फिज्म (Polymorphism): "एक नाम, कई रूप।"
जब आप pool.execute(Runnable task) कहते हैं, तो आप Runnable टाइप का उपयोग कर रहे होते हैं। यह Runnable टाइप एक बार में ClientHandler हो सकता है, अगली बार TaskScheduler हो सकता है। यह एक ही कोड को अलग-अलग प्रकार के ऑब्जेक्ट्स के साथ काम करने की अनुमति देता है।
आपके Runnable इंटरफ़ेस का अंतिम निष्कर्ष: Runnable का उपयोग इसलिए किया जाता है ताकि आप अपने टास्क (जैसे ClientHandler) को Java के थ्रेडिंग फ्रेमवर्क (Thread या ExecutorService) से अलग रख सकें।
ClientHandler बताता है क्या करना है (implements Runnable)।
ExecutorService बताता है कब और कैसे चलाना है (.execute())।
इंटरफ़ेस 'क्या' और 'कैसे' को अलग करके कोड को अधिक साफ़, लचीला और व्यवस्थित बनाता है।
PrintWriter out (लिखने के लिए) और BufferedReader in (पढ़ने के लिए) I/O स्ट्रीम सेट की जाती हैं।
while (in.readLine() != null) थ्रेड क्लाइंट से संदेशों की लाइनों को पढ़ने के लिए इस लूप में प्रवेश करता है। in.readLine() यहाँ ब्लॉक हो जाता है और क्लाइंट के डेटा भेजने का इंतज़ार करता है।
MAIN THREAD V/S WORKING THREAD -->maan lo thread pool me total 10 threads h to unnme se 1 main thread hota h and baki 9 working thread ese ??
नहीं, यह एक आम गलतफ़हमी है! थ्रेड पूल (Thread Pool) और मुख्य थ्रेड (Main Thread) पूरी तरह से अलग-अलग चीज़ें हैं।
आपके उदाहरण में:
मुख्य थ्रेड (Main Thread): यह वह थ्रेड है जो Server.java के main() मेथड को चलाता है। यह थ्रेड शुरू से अंत तक केवल ServerSocket.accept() पर कनेक्शन स्वीकार करने और उन्हें पूल को सौंपने का काम करता है (आपका "दलाल")। यह थ्रेड कभी भी पूल का हिस्सा नहीं होता है।
थ्रेड पूल (Thread Pool): यह Executors.newFixedThreadPool(10) द्वारा बनाया गया एक अलग संग्रह है।
सही व्यवस्था (Correct Arrangement): जब आप 10 थ्रेड्स का पूल बनाते हैं, तो आपके पास सिस्टम में कुल मिलाकर कम से कम 11 थ्रेड्स होते हैं:
थ्रेड का प्रकार संख्या (आपके उदाहरण में) कार्य (Job) मुख्य थ्रेड (Main Thread) 1 क्लाइंट कनेक्शन स्वीकार करना और उन्हें पूल को सौंपना। वर्कर थ्रेड्स (Worker Threads) 10 ClientHandler टास्क को चलाना—यानी क्लाइंट से डेटा पढ़ना, प्रोसेस करना और लिखना।
Export to Sheets संक्षेप में: 10 थ्रेड का पूल = 10 वर्कर थ्रेड्स।
मुख्य थ्रेड = 1 (पूल के बाहर)।
मुख्य थ्रेड और वर्कर थ्रेड्स दोनों समानांतर (parallel) में चलते हैं। मुख्य थ्रेड स्वीकार करता है, और 10 वर्कर थ्रेड्स बातचीत करते हैं।
yani thread pool ke andr sbhi wroking threads hi hote h main thread thread pool kaa hissa nhi hota
RELATION BW CONSTRUCTOR AND OBJECT -->क्लास: "यह क्या है और कैसे काम करेगा" का ब्लूप्रिंट।
इंटरफ़ेस: "यह क्या कर सकता है" का कॉन्ट्रैक्ट, जिसमें काम को कैसे करना है, यह लागू करने वाली क्लास पर छोड़ दिया जाता है।
अब, आपके सवाल पर आते हैं: कंस्ट्रक्टर और ऑब्जेक्ट के बीच क्या संबंध है?
कंस्ट्रक्टर (Constructor) और ऑब्जेक्ट (Object) का संबंध जन्मदाता (Creator) और सृष्टि (Creation) जैसा है। कंस्ट्रक्टर एक स्पेशल मेथड है जिसका मुख्य उद्देश्य किसी क्लास का ऑब्जेक्ट बनाना और उसे शुरू करना (initialize) है।
कंस्ट्रक्टर और ऑब्जेक्ट का संबंध 🛠️ यहां कंस्ट्रक्टर और ऑब्जेक्ट के बीच का सीधा संबंध बताया गया है:
- कंस्ट्रक्टर (Constructor): द बिल्डर (The Builder) कंस्ट्रक्टर क्लास के अंदर एक विशेष प्रकार की मेथड है जिसका नाम हमेशा क्लास के नाम जैसा ही होता है, और इसका कोई रिटर्न टाइप (जैसे void) नहीं होता।
उद्देश्य: किसी क्लास के नए ऑब्जेक्ट को मेमोरी आवंटित (allocate) करना और उसके वैरिएबल्स (डेटा) को प्रारंभिक मान (initial values) देना।
जन्म का क्षण: जब आप new कीवर्ड का उपयोग करते हैं, तो कंस्ट्रक्टर अपने आप कॉल होता है।
- ऑब्जेक्ट (Object): द क्रिएशन (The Creation) ऑब्जेक्ट एक क्लास का वास्तविक रूप (real-world instance) है। यह मेमोरी में मौजूद होता है और आप इसका उपयोग कर सकते हैं।
उद्देश्य: क्लास के ब्लूप्रिंट में परिभाषित डेटा और व्यवहार को धारण करना।
THREAD POOL V/S EVENT LOOP -->
- Thread Pool Model 🧵 The Thread Pool model is based on multi-threading and blocking I/O. It achieves concurrency by executing multiple tasks simultaneously (or seemingly simultaneously) on different threads.
Feature Description Concurrency Mechanism Threading (Parallelism/Concurrency). A pool of pre-spawned threads is maintained. I/O Model Blocking I/O. When a thread starts an I/O operation (like reading from a disk or a network socket), the thread stops (blocks) and waits until the operation is complete. Scaling Vertical Scaling. To handle more load, you must add more threads. Best For CPU-bound tasks (heavy calculations, data processing) where tasks spend most of their time using the CPU rather than waiting for external resources. Example Environments Java (using ExecutorService), C#, traditional Python (with threads).
Export to Sheets Analogy: A call center with 10 agents (threads). Each agent handles one customer (task) from start to finish. If the customer goes on hold (I/O block), the agent waits with them, tying up that resource.
- Event Loop Model 🔄 The Event Loop model is based on single-threading (or a few threads) and non-blocking I/O. It achieves high concurrency and efficiency by avoiding blocking operations entirely.
Feature Description Concurrency Mechanism Asynchronous callbacks. A single thread processes a queue of events/tasks very quickly. I/O Model Non-blocking I/O. When a task starts an I/O operation, it does not wait. It registers a callback and immediately returns to process the next task. When the I/O is done, the kernel notifies the loop, and the callback is executed. Scaling Horizontal Scaling. Handles high concurrency efficiently using minimal threads. Best For I/O-bound tasks (network requests, database queries, file access) where tasks spend most of their time waiting for external operations. Example Environments Node.js (V8 engine), Python (asyncio), browsers (JavaScript).
Export to Sheets Analogy: A restaurant waiter (single thread). The waiter takes an order (task), immediately hands it to the kitchen (I/O operation), and starts serving another table (next task). The waiter only returns to the original task when the kitchen notifies them the food is ready (I/O complete).
Comparison Summary Feature Thread Pool Event Loop Primary Resource Threads (many threads) CPU (one or few threads) I/O Handling Blocking. Thread waits for I/O. Non-blocking (Asynchronous). Thread continues processing while I/O happens in the background. Overhead Higher context switching overhead between many threads. Very low overhead; minimal context switching. Complexity Easier for synchronous code logic. Requires careful synchronization (locks) to manage shared data. Code logic can be complex due to callbacks and asynchronous flow. CPU-Bound Tasks Ideal. Can utilize all CPU cores simultaneously (true parallelism). Poor. A single heavy calculation will block the entire loop, stopping all other tasks.