परिचय: बुनियादी synchronized से आधुनिक उपकरणों तक
जावा उन कुछ भाषाओं में से एक है जिसमें जन्म से ही मल्टीथ्रेडिंग के लिए अंतर्निहित समर्थन है। हालांकि, शुरुआती स्तर पर डेवलपर्स अक्सर synchronized कीवर्ड और सरल Thread तक ही सीमित रहते हैं। वास्तविक उच्च-लोड सिस्टम में यह पर्याप्त नहीं है: लॉक डेडलॉक उत्पन्न करते हैं, और थ्रेड का सरल निर्माण JVM को ओवरलोड कर देता है।
इस लेख में, हम जावा में समवर्तीता के उन्नत तंत्रों पर चर्चा करेंगे जो कुशल, स्केलेबल और सुरक्षित मल्टीथ्रेडेड कोड लिखने में सक्षम बनाते हैं। यह सामग्री उन डेवलपर्स के लिए है जो पहले से ही मल्टीथ्रेडिंग की मूल बातों (मॉनिटर, wait/notify, volatile) से परिचित हैं।
हम चार प्रमुख विषयों पर विचार करेंगे: पुनरावर्ती कार्यों के लिए Fork/Join Framework, एसिंक्रोनस प्रोग्रामिंग के लिए CompletableFuture, ReadWriteLock के विकल्प के रूप में StampedLock, और ThreadLocal का उचित उपयोग।
1. Fork/Join Framework: कार्यों का पुनरावर्ती विभाजन
Fork/Join Framework जावा 7 में आया था और यह उन कार्यों को हल करने के लिए मल्टी-कोर प्रोसेसर के कुशल उपयोग के लिए डिज़ाइन किया गया है जिन्हें उप-कार्यों (divide and conquer) में विभाजित किया जा सकता है। मुख्य वर्ग ForkJoinPool है, जो "कार्य-चोरी" (work-stealing) तंत्र का उपयोग करता है।
1.1. कार्य सिद्धांत
मैन्युअल रूप से थ्रेड बनाने के बजाय, आप कार्य को RecursiveTask (यदि रिटर्न वैल्यू है) या RecursiveAction (बिना रिटर्न के) के उत्तराधिकारी के रूप में वर्णित करते हैं। फिर आप कार्य को पुनरावर्ती रूप से भागों में विभाजित करते हैं, जब तक कि वे सीधे निष्पादन के लिए पर्याप्त छोटे न हो जाएं।
1.2. उदाहरण: ऐरे का समानांतर योग
import java.util.concurrent.RecursiveTask;import java.util.concurrent.ForkJoinPool;
public class SumTask extends RecursiveTask<Long> { private static final int THRESHOLD = 10_000; private final int[] array; private final int start, end;
public SumTask(int[] array, int start, int end) { this.array = array; this.start = start; this.end = end; }
@Override protected Long compute() { if (end - start <= THRESHOLD) { long sum = 0; for (int i = start; i < end; i++) { sum += array[i]; } return sum; } int mid = start + (end - start) / 2; SumTask left = new SumTask(array, start, mid); SumTask right = new SumTask(array, mid, end); left.fork(); // बाएँ भाग को एसिंक्रोनस रूप से चलाएँ long rightResult = right.compute(); // दाएँ भाग की गणना वर्तमान थ्रेड में करें long leftResult = left.join(); // बाएँ भाग के परिणाम की प्रतीक्षा करें return leftResult + rightResult; }
public static void main(String[] args) { int[] data = new int[1_000_000]; for (int i = 0; i < data.length; i++) data[i] = i;
ForkJoinPool pool = ForkJoinPool.commonPool(); long result = pool.invoke(new SumTask(data, 0, data.length)); System.out.println("योग: " + result); }}महत्वपूर्ण: ForkJoinPool.commonPool() का उपयोग करें - यह पहले से ही कोर की संख्या के अनुसार अनुकूलित है। आवश्यकता के बिना स्वयं के पूल बनाने से बचें।
2. CompletableFuture: callback-अड के बिना एसिंक्रोनसी
जावा 8 से पहले, एसिंक्रोनस प्रोग्रामिंग Future के माध्यम से संभव थी, लेकिन यह सिंक्रोनस (ब्लॉकिंग कॉल get()) था। CompletableFuture इस समस्या को हल करता है, एसिंक्रोनस संचालन की संरचना के लिए एक शक्तिशाली कार्यात्मक API प्रदान करता है।
2.1. बुनियादी परिदृश्य
आप CompletableFuture बना सकते हैं, कार्यों को एसिंक्रोनस रूप से चला सकते हैं और उन्हें श्रृंखलाओं में जोड़ सकते हैं:
import java.util.concurrent.CompletableFuture;
public cl